Python核心編程學習筆記(1)
迭代器和iter()函數
1,定義:python2.2被加入,它爲類序列對象提供了一個類序列接口,它們是一組數據結構,你可以利用它們的索引從0開始一直迭代到序列的最後一個條目.用“計數”的方法迭代序列是很簡單的。Python的迭代無縫地支持序列對象,而且還允許程序員迭代非序列對象,包括用戶自定義對象。
迭代器用起來很靈巧,你可以迭代不是序列但表現出序列行爲的對象,例如字典的key,一個文件的行,等等,當你使用循環迭代一個對象條目時,你幾乎不可能分辨出它是迭代器還是序列,你也不必關注這些,因爲python讓它像一個序列那樣操作。
2,迭代器優勢
a) 提供了可擴展的迭代器接口。
b) 對列表迭代帶來了性能上的增強。
c) 在字典迭代中性能提升。
d) 創建真正的迭代接口,而不是原來的隨機訪問。
e) 與所有已經存在的用戶定義的類以及擴展的模擬序列和映射的對象向後兼容。
f) 迭代非序列集合如映射和文件時可以創建更簡潔可讀的代碼。
3,迭代方法
迭代器有一個next()方法的對象,而不是通過索引來計數,當你或是循環機制如for語句需要下一個項時,調用迭代器的next()方法就可以獲得它。條目全部取出後,會引發一個StopIteration異常,這不是錯誤,是告訴外部調用者,迭代完成。
限制:不能向後移動,不能回到開始,也不能複製一個迭代器,若你要再次或者是同時迭代同個對象,你只能去創建另一個迭代器對象。不過這並不用擔心,因爲還有其他工具幫助你使用迭代器。
resersed()內建函數返回一個反序列的迭代器。Enumerate()內建函數也返回迭代器。此外兩個新的內建函數,any()和all(),在版本2.5新增,若迭代器中某個/所有條目的值均爲真則返回真。Python提供一整個itertools模塊,包含各種有用的迭代器。
4,使用迭代器
a) 序列
>>> myTuple = (123,'xyz',45.67)
>>> i = iter(myTuple)
>>> i.next()
123
>>> i.next()
'xyz'
>>> i.next()
45.670000000000002
>>> i.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
正常使用時,用try-except捕獲異常
>>> i = iter(myTuple)
>>> while True:
... try:
... j = i.next()
... except StopIteration:
... break;
... print j,
...
123 xyz 45.67
>>>
>>>
b) 字典
字典和文件是另外兩個可迭代的python數據類型,字典的迭代會遍歷它的鍵(keys)
語句for eachKey in myDict.keys()可以縮寫爲for eachKey inmyDict
>>> legends ={('Poe','author'):(1809,1849,1976),
... ('Gaudi','architect'):(1852,1906,1987),
...('Freud','psychoanalyst'):(1856,1939,1990)
... }
>>>
>>> for eachLegend in legends:
... print 'Name: %s\tOccupation: %s' % eachLegend
... print ' Birth: %s\tDeath:%s\tAlbum: %s\n' % legends[eachLegend]
...
Name: Poe Occupation: author
Birth: 1809 Death: 1849 Album: 1976
Name: Gaudi Occupation: architect
Birth: 1852 Death: 1906 Album: 1987
Name: Freud Occupation: psychoanalyst
Birth: 1856 Death: 1939 Album: 1990
>>>
>>>
此外,python還引進三個新的內建字典方法來定義迭代:myDict.iterkeys() (通過keys迭代)、myDict.itervalues()(通過values迭代)、myDict.iteritems()(通過key/value對迭代)。
In操作符也可以用於檢查字典的key是否存在,布爾表達式myDict.has_key(anykey)可簡寫爲anyKey inmyDict
c) 文件
文件對象生成的迭代器會自動調用readline()方法。可以訪問文件的所有行,並且可以使用簡單的for eachLine in myFile替換for eachLine in myFile.readlines():
>>> myFile = open('test.txt')
>>> for eachLine in myFile:
... print eachLine,
...
font-name: courier newfont-size: 10
>>>
>>> myFile.close()
>>>
5,創建迭代器
使用ietr()
a) Iter(obj)
b) Iter(func,sentinel)
若你傳遞一個參數給iter(),它會檢查是不是序列,若是,則根據索引從0一直迭代序列結束。另一個創建迭代器的方法是使用類,一個實現了__iter__()和next()方法的類可以作爲迭代器使用。
若是兩個參數,它會重複調用func,直到迭代器的下個值等於sentinel
列表解析
1, 列表解析來自函數式編程Haskell,可以用來動態創建列表
2, 函數式編程特性,如lambda,map(),filter(),map()對所有的列表成員應用一個操作,filter()基於一個條件表達式過濾列表成員。最好lambda允許快速創建單行函數對象
語法:[expr for iter_var in iterable]
>>>map(lambda x: x** 2, range(6))
[0, 1, 4,9, 16, 25]
>>>
>>>[x ** 2 for x in range(6)] #效率更高
[0, 1, 4,9, 16, 25]
>>>
擴展語法:[expr for iter_var in iterable if cond_expr]
挑選序列中的奇數:
>>>seq = [11,10,9,9,10,10,9,8,23,9,7,18,12,11,12]
>>>filter(lambda x: x % 2, seq)
[11, 9,9, 9, 23, 9, 7, 11]
>>>
>>>[x for x in seq if x % 2]
[11, 9,9, 9, 23, 9, 7, 11]
>>>
>>>
迭代三行五列的矩陣:
>>> [(x+1,y+1) for x in range(3) for y in range(5)]
[(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (2, 1), (2, 2),(2, 3), (2, 4), (2, 5), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5)]
>>>
計算文件所有非空白字符數目:
>>>f = open('test.txt','r')
>>>len([word for line in f for word in line.split()])
4
>>>
快速計算文件大小:
>>>os.stat('test.txt').st_size
35
>>>
>>>
>>>f.seek(0)
>>>sum([len(word) for line in f for word in line.split()])
32
>>>
>>>
seek(0)回到文件頭部
生成器表達式
特定的函數,允許你返回一個值,然後“暫停”代碼的執行,稍後回覆
列表解析的不足是必要生成所有的數據,以創建整個列表,這可能對大量數據的迭代器有負面效應。生成器表達式通過結合列表解析和生成器解決了這個問題。
生成器與列表解析非常相似,語法恩相同。不過它並不真正創建數字列表,而是返回一個生成器,這個生成器在每次計算出一個條目後,把這個條目“產生”(yield)出來。生成器表達式使用“延遲計算”,所以它在使用內存上更有效。
列表解析:
[expr for ietr_var in iterable if cond_expr]
生成器表達式:
(expr for iter_var in ietrable id cond_expr)
生成器是一個內存使用更友好的結構
更高效的方法:
>>> f.seek(0)
>>> sum(len(word) for line in ffor word in line.split())
32
>>>
>>>
>>> rows = [1,2,3,17]
>>> def cols():
... yield 56
... yield 2
... yield 1
...
>>>
>>> pairs = ((i, j) for i in rowsfor j in cols())
>>> for pair in pairs:
... print pair
...
(1, 56)
(1, 2)
(1, 1)
(2, 56)
(2, 2)
(2, 1)
(3, 56)
(3, 2)
(3, 1)
(17, 56)
(17, 2)
(17, 1)
>>>
**改進一個尋找文件最長的行的程序代碼:
>>> import os
>>> os.listdir(os.getcwd())
['LogAnaly.py', 'query_autoconf.py','insertSort.py', 'sdxl_efun_LogAnaly.pl', 'test.txt']
>>>
>>>
>>> f = open('/etc/motd','r')
>>> longest = 0
>>> while True:
... linelen = len(f.readline().strip())
... if not linelen:
... break
... if linelen > longest:
... longest = linelen
>>> f.close()
>>>return longest
更老一點:
Import string
..
len(string.strip(f.readline()))
首選方法:
f = open(‘/etc/passwd’,’r’)
longest = 0
allLines = f.readlines()
f.close()
for line in allLines:
linelen = len(line.strip())
if linelen >longest:
longest = linelen
return longest
再改進:
f = open(‘/etc/passwd’,’r’)
longest = 0
allLines = [x.strip() for x inf.readlines()]
f.close()
for line in allLines:
linelen = len(line)
if linelen >longest:
longest = linelen
return longest
但是以上處理大文件時會讀取文件所有行,所以我們使用了迭代器,文件本身成爲它的迭代器,不需要調用readlines()方法:
f = open(‘/etc/passwd’,’r’)
allLineLens = [len(x.strip()) for x in f]
f.close()
return max(allLineLens)
這裏唯一問題是一行一行迭代f時,列表解析需要文件的所有行讀取到內存中,然後生成列表,因此可以用生成器代替列表解析,然後移到max()函數裏:
f = open(‘/etc/passwd’,’r’)
longest = max (len(x.strip()) for x in f)
f.close()
return longest
最後可簡化爲:
return max(len(x.strip()) for x in open(‘/etc/passwd’))
相關模塊:itertools