python簡單筆記
叠代器
叠代是Python最強大的功能之一,是訪問集合元素的一種方式。
叠代器是一個可以記住遍歷的位置的對象。
叠代器對象從集合等第一個元素開始訪問,直到所有的元素被訪問結束,叠代器只能往前不會後退。
叠代器有兩個基本的方法:iter()和next()
字符串,列表或元組對象都可以用於創建叠代器。
叠代器的一大優點是不要求事先準備好整個叠代過程中所有的元素。叠代器僅僅在叠代到某個元素時才計算該元素,而在這之前或之後,元素可以不存在或者被銷毀。這個特點使得它特別適合用於遍歷一些巨大的或是無限的集合,比如幾個G的文件
特點:
- 訪問者不需要關心叠代器內部的結構,僅需通過next()方法不斷去取下一個內容
- 不能隨機訪問集合中的某個值 ,只能從頭到尾依次訪問
- 訪問到一半時不能往回退
- 便於循環比較大的數據集合,節省內存
>>> list = [1,2,3,4] >>> it = iter(list) # 創建叠代器對象 >>> print(next(it)) # 輸出叠代器的下一個元素 1 >>> print(next(it)) 2
叠代器對象可以使用常規for語句進行遍歷:
list=[1,2,3,4] it = iter(list) # 創建叠代器對象 for x in it: print (x)
執行以上程序,輸出結果如下:
1 2 3 4
也可以使用 next() 函數:
import sys # 引入 sys 模塊 list=[1,2,3,4] it = iter(list) # 創建叠代器對象 while True: try: print (next(it)) except StopIteration: sys.exit()
執行以上程序,輸出結果如下:
1 2 3 4
凡是能夠用for循環遍歷的數據結構和對象統統可以稱為可叠代的,即Iterable。我們現在接觸到Iterable數據結構有list、tuple、dict、string、set,還有生成器generator(下面將講到)。
但是,他們之間有什麽區別呢?generator不但可以使用for循環,還可以被next()
函數不斷調用並返回下一個值,直到返回StopIteration
錯誤。
能夠被next()
函數不斷調用並返回下一個值的對象稱為叠代器Iterator。generator是Iterable,同時也是一個Iterator;而list、tuple、str、dict不是Iterator。
當然,可以通過isinstance()
判斷一個對象是否為Iterator。
>>> from collections import Iterator >>> isinstance((x*x for x in range(10)), Iterator) True >>> isinstance([],Iterator) False >>> isinstance({},Iterator) False >>> isinstance("ABC",Iterator) False
如果想讓list、tuple、str、dict等Iterable對象轉變成Iterator,用iter()函數能夠讓Iterable對象變為Iterator
>>> isinstance(iter("ABC"),Iterator) True >>> i = iter("ABC") >>> next(i) ‘A‘ >>> next(i) ‘B‘ >>> next(i) ‘C‘
Iterable和Iterator對象到底有什麽區別呢?
實際上,python中Iterator對象表示的是一個數據流,Iterator對象可以被next()函數不斷調用返回下一個值,直到數據全部被返回。Iterator對象數據流可以被看作是一個序列,但是這個序列的長度是不可提前預知的,因為只能通過next()或者for循環按照需要調用下一個數據,所以Iterator對象的計算是一種惰性計算。
正是因為這樣的特性,Iterator可以表示一個無限大的數據流,譬如全體自然數,而list是無法完成這樣的任務的。
列表生成式
在講到生成器之前我們先來講一下列表生成式
列表生成式,顧名思義,就是用來生成列表的。以前我們生成列表的時候,一般都是先定義一個list = [] ,再用for循環將元素一個一個append到list中。
簡單列表的生成,可以使用list(range(1,100)),那麽如果需要生成[1*1,2*2,3*3....]呢?
>>> [x*x for x in range(10)] [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
同時,還可以添加一些更多的要素,判斷使用符合要求的x參與計算。
>>> [x*x for x in range(10) if x%2==0] [0, 4, 16, 36, 64]
使用多重循環還可以生成全排列
>>> [x+y for x in ‘ABC‘ for y in ‘LMN‘] [‘AL‘, ‘AM‘, ‘AN‘, ‘BL‘, ‘BM‘, ‘BN‘, ‘CL‘, ‘CM‘, ‘CN‘] >>> >>> [x+y+z for x in ‘ABC‘ for y in ‘LMN‘ for z in ‘OPQ‘ if z==‘Q‘] [‘ALQ‘, ‘AMQ‘, ‘ANQ‘, ‘BLQ‘, ‘BMQ‘, ‘BNQ‘, ‘CLQ‘, ‘CMQ‘, ‘CNQ‘]
生成器
通過列表生成器,我們可以創造一個列表。但是,受到內存容量的限制,列表的容量肯定是有限的,如若我們只需要使用列表中的一部分,就不需要一下子生成很大的列表,直接按需生成就好了,列表生成器就提供了這樣的功能。
生成器相當於是一個生成列表元素的算法,有了這個算法,就不用一下子生成很多元素了,只需要在需要的時候生成就可以了,節省了很多空間。在Python中,這種一邊循環一遍計算的機制,稱為:generator。
創建一個generator只需要把列表生成器的[]改成()就可以了。
>>> [x*x for x in range(10)] [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> (x*x for x in range(10)) <generator object <genexpr> at 0x00000259A50BD308>
獲取generator裏面的元素,可以使用next()函數生成generator的下一個元素。
>>> g=(x*x for x in range(10)) >>> next(g) 0 >>> next(g) 1 >>> next(g) 4 . . . >>> next(g) 81 >>> next(g) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
一直調用next()函數就可以遍歷generator的所有元素,直到拋出StopIteration錯誤。
但是最常用的當然還是for循環,畢竟generator也是Iterator的對象。
>>> g=(x*x for x in range(5)) >>> for element in g: ... print(element) ... 0 1 4 9 16
一般我們創建一個generator後,基本上不會調用next(),而是通過for循環來叠代它,並且不需要關心StopIteration的錯誤。
有時候,推算元素的算法很復雜,並不能通過列表生成器實現,而需要通過函數打印出來,譬如菲波那切數列。
#!/usr/bin/env python def fib(Max): n = 0 a,b = 0,1 while n < Max: print(b) a,b = b,a+b n = n + 1 fib(5) 結果: 1 1 2 3 5
在Python中,使用了yield的函數被稱為生成器(generator)。
跟普通函數不同的是,生成器是一個返回叠代器的函數,只能用於叠代操作,更簡單點理解生成器就是一個叠代器。
在調用生成器運行的過程中,每次遇到yield時函數會暫停並保存當前所有燈運行信息,返回yield的值。並在下一次執行next()方法時從當前位置繼續運行。
#!/usr/bin/env python def fib(Max): n = 0 a,b = 0,1 while n < Max: yield b a,b = b,a+b n = n + 1 g = fib(5) print(next(g)) print(next(g)) for i in g: print(i) 結果: 1 1 2 3 5
在函數的執行過程中,遇到return才會返回,但是變成了generator的函數,遇到了yield就會中斷,並且返回yield的參數。
那麽,遇到了yield就會中斷並返回,我們能不能通過yield傳一些參數進去呢?
#!/usr/bin/env python def fib(Max): n = 0 a,b = 0,1 while n < Max: e = yield b print(e) a,b = b,a+b n = n + 1 g = fib(5) print(next(g)) print(next(g)) print(g.send("the third element")) print(g.send("forth")) 結果: 1 None 1 the third element 2 forth 3
在上面的generator定義中,使用了e = yield b語句,這樣遇到yield返回b的同時,還能夠通過g.send(arg)傳入一個參數賦值給e,這樣,就可以通過generator進行參數傳遞了。
>>> def fib(Max): ... n = 0 ... a,b = 0,1 ... while n < Max: ... e = yield b ... print(e) ... a,b = b,a+b ... n = n + 1 ... >>> g = fib(5) >>> g.send("the first element") Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can‘t send non-None value to a just-started generator >>> g.send(None) 1
註意,看上面的代碼,為什麽會報錯?說明啟動一個generator的時候,只能傳入None作為參數,而generator就相當於next(g)!
2.裝飾器
原則:
1、不能修改被裝飾函數的源代碼。
2、不能修改被裝飾函數的調用方式。
3、不能改變被裝飾函數的執行結果。
裝飾器對被裝飾函數是透明的。
如何理解裝飾器
1、函數即“變量”
2、高階函數
a:把一個函數名作為實參傳遞給另外一個函數
b:返回值中包含函數名
3、嵌套函數
在一個函數體內聲明另一個函數稱為函數的嵌套
參考鏈接:http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014318435599930270c0381a3b44db991cd6d858064ac0000
裝飾器(decorator)是一種高級的Python語法。裝飾器可以對一個函數,方法或者類加工。
由於函數也是一個對象,而且函數對象可以被賦值給變量,所以,通過變量也能調用該函數。
>>> def now(): ... print(‘2017-05-01‘) ... >>> f = now >>> f() 2017-05-01
函數對象有一個__name__屬性,可以拿到函數的名字:
>>> now.__name__ ‘now‘ >>> f.__name__ ‘now‘
現在,假設我們要增強now()函數的功能。比如,在函數調用前後自動打印日誌,但是又不希望修改now()函數的定義,這種在代碼運行期間動態增加功能的方式,就稱之為:裝飾器(decorator)。
本質上,decorator就是一個返回函數的高階函數。所以,我們要定義一個能打印日誌的decorator,可以定義如下:
def log(func): def wrapper(*args, **kwargs): print("call %s():" % func.__name__) return func(*args, **kwargs) return wrapper
觀察上面的log,因為它是一個decorator,所以接受一個函數作為參數,[email protected],吧decorator置於函數的定義處:
@log # 把這句放在這兒就相當於執行了 now = log(now) def now(): print("2017-05-01")
調用now()函數,不僅會運行now()函數本身,還會運行now()函數前打印一行日誌:
>>> now() call now(): 2017-05-01
[email protected]()函數的定義處,就相當於執行了語句:
now = log( now )
由於log()是一個decorator,返回一個函數,所以,原來的now()函數仍然存在,只是現在同名的now變量指向了新的函數,於是調用now()將執行新函數,即在log()函數中返回的wrapper()函數。
如果decorator本身需要傳入參數,那就需要編寫一個返回decorator的高階函數,寫出來會更復雜。比如,要自定義log的文本:
>>> def log(text): ... def decorator(func): ... def wrapper(*args, **kwargs): ... print("%s %s():" % (text, func.__name__)) ... return func(*args, **kwargs) ... return wrapper ... return decorator ... >>> @log("execute") ... def now(): ... print("2017-05-01") ... >>> now() execute now(): 2017-05-01
和兩層嵌套的decorator相比,3層嵌套的效果是這樣的:
>>> now = log("execute")(now)
我們來剖析上面的語句,首先執行log(‘execute‘),返回的是的decorator函數,在調用返回的函數,參數是now函數,返回值最終是wrapper函數。
以上兩種decorator的定義都沒有問題,但還差最後一步。因為我們講了函數也是對象。它有__name__等屬性,但你去看經過decorator裝飾之後的函數,它們的__name__已經從原來的‘now‘變成了‘wrapper‘:
>>> now.__name__ ‘wrapper‘
因為返回的那個wrapper()函數名字就是‘wrapper‘,所以,需要把原始函數的__name__等屬性復制到wrapper()函數中,否則,有些依賴函數簽名的代碼執行就會出錯。
不需要編寫wrapper.__name__ = func.__name__這樣的代碼,Python內置的functools.wraps就是幹這個事的,所以,一個完整的decorator的寫法如下:
import functools def log(func): @functools.wraps(func) def wrapper(*args, **kw): print(‘call %s():‘ % func.__name__) return func(*args, **kw) return wrapper @log("execute") def now(): print("2017-05-01") now()
或者針對帶參數的decorator:
import functools def log(text): def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print("%s %s():" % (text, func.__name__)) return func(*args, **kwargs) return wrapper return decorator @log("execute") def now(): print("2017-05-01") now()
python簡單筆記