1、List Conprehensions
列表生成式是python中內(nèi)置的用來創(chuàng)建list
的表達(dá)式、
如果我們要生成簡(jiǎn)單的list可以直接使用range函數(shù)來完成。
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
但是有時(shí)候我們要生成復(fù)雜的list怎么辦呢?比如我們生成一個(gè)[1^2, 2^2, 3^2...]
的list。
>>> my_list = []
>>> for item in range(10):
... my_list.append(item * item)
...
>>> my_list
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>>
上面的代碼都不夠簡(jiǎn)潔,python中的列表生成式可以一行解決。
>>> [x * x for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>>
在書寫生成式表達(dá)式時(shí),將要生成的元素放在前面,后面是for循環(huán)。
還可以在創(chuàng)建列表的同時(shí)進(jìn)行簡(jiǎn)單的過濾。
>>> [x * x for x in range(10) if x%2 == 0]
[0, 4, 16, 36, 64]
>>>
還可以實(shí)現(xiàn)全排列
>>> [(x,y) for x in '123' for y in '456']
[('1', '4'), ('1', '5'), ('1', '6'), ('2', '4'), ('2', '5'), ('2', '6'), ('3', '4'), ('3', '5'), ('3', '6')]
>>>
2、Iterables
當(dāng)我們創(chuàng)建了一個(gè)列表,我們可以依次遍歷它,依次遍歷的過程就是迭代。
>>> my_list = [1, 2, 3]
>>> for i in my_list:
... print i
...
1
2
3
my_list就是一個(gè)可迭代對(duì)象,當(dāng)我們使用上面說的列表生成式創(chuàng)建一個(gè)列表時(shí),該列表也是一個(gè)可迭代的。
>>> my_list = [x * x for x in range(3)]
>>> for i in my_list:
... print i
...
0
1
4
對(duì)于任何可迭代的對(duì)象都可以使用for in
來進(jìn)行迭代。
但是這樣做有一個(gè)缺點(diǎn)就是:當(dāng)我們的數(shù)據(jù)非常多的時(shí)候比如一個(gè)大列表,這個(gè)列表將會(huì)被讀到內(nèi)存中,會(huì)占用非常多的內(nèi)存,有時(shí)候硬件是達(dá)不到的。
3、 Generators
對(duì)一個(gè)包含大量數(shù)據(jù)的列表,不僅占用很大的內(nèi)存,如果我們使用到的就只有某一小部分?jǐn)?shù)據(jù)就會(huì)導(dǎo)致內(nèi)存的浪費(fèi)。
如果列表中的元素可以根據(jù)某種算法推導(dǎo)出來,而不是將所有元素都存放在列表中,從而可以節(jié)省很多內(nèi)存空間。這樣一邊循環(huán)一邊計(jì)算的機(jī)制叫做生成器(Generator)。
創(chuàng)建一個(gè)Generator直接可以將上面的列表生成式的[]
換成()
就可以了。python中提供了生成器表達(dá)式,它是對(duì)列表和生成器的一種泛化,生成器表達(dá)式在運(yùn)行的時(shí)候,并不會(huì)將整個(gè)輸出序列都呈現(xiàn)出來,而是會(huì)估值為迭代器,這個(gè)迭代器每次可以根據(jù)生成器表達(dá)式產(chǎn)生一項(xiàng)數(shù)據(jù)。
>>> L1 = [x * x for x in range(10)]
>>> L2 = (x * x for x in range(10))
>>> L1
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> L2
<generator object <genexpr> at 0x000000000347F7E0>
>>>
從打印結(jié)果可以看出L2是一個(gè)Generator,可以使用next()
方法打印每一個(gè)元素。
>>> L2.next()
0
>>> L2.next()
1
>>> L2.next()
4
>>> L2.next()
9
>>> L2.next()
16
>>> L2.next()
....
>>> L2.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
如果沒有元素了就會(huì)拋出異常。
Generator也是可迭代的,但是只能迭代一次。
>>> L2 = (x * x for x in range(10))
>>> for i in L2:
... print i
...
0
1
4
9
16
25
36
49
64
81
>>>
4、生成器和迭代器區(qū)別
迭代器是一個(gè)更普遍的概念,如果一個(gè)類中有next()
方法和返回自身return self
的__iter__()
方法,這個(gè)類的對(duì)象就是一個(gè)Iterator
。
所有的生成器是一個(gè)迭代器,但是反過來就不是。生成器通過調(diào)用具有一個(gè)或多個(gè)yield表達(dá)式(在Python 2.5和更早版本中的yield語句)的函數(shù)構(gòu)建。
>>> def squares(start, stop):
... for i in range(start, stop):
... yield i * i
...
>>>
>>> generator = squares(1, 3)
>>> generator
<generator object squares at 0x000000000347F7E0>
>>>
當(dāng)然還可以使用生成器表達(dá)式直接生成一個(gè)Generator。
但是有時(shí)候你需要一個(gè)定制的迭代器,定義一個(gè)類,該類實(shí)現(xiàn)next() 和 __iter__()
方法就可以。
class Squares(object):
def __init__(self, start, stop):
self.start = start
self.stop = stop
def __iter__(self):
return self
def next(self):
if self.start >= self.stop:
raise StopIteration
current = self.start * self.start
self.start += 1
return current
5、使用yield
關(guān)鍵字創(chuàng)建生成器
如果一個(gè)函數(shù)包含了yield
關(guān)鍵字,這個(gè)函數(shù)就是一個(gè)生成器。但是Generator和函數(shù)的執(zhí)行流程是不一樣的。函數(shù)是順序執(zhí)行,遇到return語句或者執(zhí)行完畢后就返回,如果變成了Generator后,每次調(diào)用next()函數(shù)開始執(zhí)行,遇到yield
返回,下次接著從上次返回位置進(jìn)行執(zhí)行。
>>> def fun():
... print 'run 1'
... yield 1
... print 'run 2'
... yield 2
...
>>> g = fun()
>>> g.next()
run 1
1
>>> g.next()
run 2
2
>>> g.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
我們使用關(guān)鍵字yield
定義了一個(gè)Generator,在執(zhí)行的時(shí)候,遇到yield
關(guān)鍵字就會(huì)返回,再次調(diào)用就從上次返回的地方開始。