一.列表推導式
1.列表推導式是頗具python風格的一種寫法。這種寫法除了高效,也更簡短。
In [23]: {i:el for i,el in enumerate(["one","two","three"])} Out[23]: {0: 'one', 1: 'two', 2: 'three'}
enumerate是內建函數,可以讓列表獲得“下標”的屬性。
而如果不用列表推導式,上例需要這么寫
In [24]: lst = ["one","two","three"] In [25]: i = 0 In [26]: for e in lst: ....: lst[i] = '%d: %s' % (i,lst[i]) ....: i +=1 ....: In [27]: lst Out[27]: ['0: one', '1: two', '2: three']
二.迭代器
迭代器屬於一個臨時區,安排一些元素在里面,但只用用的時候才會創建一些臨時區,一旦遍歷結束則臨時區清空,再遍歷就失效了。所以說迭代器能夠減少內存的開銷。
下面用代碼來說明這句話的意思。
In [29]: import sys In [30]: i = iter(range(10000)) In [32]: id(i.__next__()) Out[32]: 4388723488 In [33]: sys.getsizeof(i) Out[33]: 48 In [34]: sys.getsizeof(i.__next__()) Out[34]: 28 In [35]: e = range(10000) In [37]: sys.getsizeof(e) Out[37]: 48 In [38]: sys.getsizeof(list(e)) Out[38]: 90112
可以看到,如果一次性把list全部加載進來,需要90112byte內存空間,如果使用迭代器迭代,僅僅需要28byte內存空間。
ps:可以被next()
函數調用並不斷返回下一個值的對象稱為迭代器。生成器都是Iterator
對象,但list
、dict
、str
雖然是Iterable
,卻不是Iterator,
list,tuple,dict,str是有可迭代屬性(惰性循環),但如果需要轉化成可迭代對象,可以用iter()來轉換,驗證方式就是看對象是否有__next__方法。
三.生成器(generator)
生成器是一種特殊的迭代器。
1.什么時候需要用生成器
其實一般情況下是不需要生成器的,只有當因為性能限制下才需要用到,比如你需要用python來read一個10g的txt文件,如果一次性把10g的文件加載到內存再處理(.read()方法),內存肯定溢出了。這里如果使用生成器,就可以把讀和處理交叉進行,比如使用(.readline和.readlines)就可以在循環讀取的同時不斷處理,這樣可以節省大量內存空間。此外,還可以使用生成器生成線程池。
(ps:如果當自己寫一個讀寫函數封裝給別人使用時,那么要考慮到文件容量問題,此時應該考慮使用生成器。)
2.生成生成器的兩種方法
2.1 第一種比較簡單,將列表推導式的[]改稱()就可以了
In [40]: g = (x*x for x in range(10))
2.2 第二種辦法就是在函數里加入yield關鍵字。yield和return有點類似,都可以用來返回值,不同的是yield遇到next()就返回,再次執行時從上次返回的yield
語句處繼續執行。
3.如何判斷一個函數是否是生成器
判斷生成器的辦法就是查看其屬性
In [41]: dir(g)
Out[41]: ['__class__', ... '__next__', ... '__repr__', '__setattr__', ]
在這里可以看到g有一個__next__的魔術方法,而這是生成器所特有的屬性,下面兩種方式調用都可以
In [42]: g.__next__()
Out[42]: 0 In [43]: g.__next__() Out[43]: 1 In [44]: next(g) Out[44]: 4