python-生成器、迭代器、閉包、裝飾器
一、生成器
1、什麼是生成器
在Python中,這種一邊迴圈一邊計算的機制,稱為生成器(generator)
2、建立生成器方法
方法1:只要把一個列表生成式的[ ]改為()
In [1]: L = [ x*2 for x in range(5)]
In [2]: L
Out[2]: [0, 2, 4, 6, 8]
In [3]: G = ( x*2 for x in range(5))
In [4]: G
Out[4]: <generator object <genexpr> at 0x000001F84309CEB8>
可以通過next()函式獲得生成器的下一個返回值
In [5]: next(G) Out[5]: 0 In [6]: next(G) Out[6]: 2 In [7]: next(G) Out[7]: 4 In [8]: next(G) Out[8]: 6 In [9]: next(G) Out[9]: 8 In [10]: next(G) --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-10-b4d1fcb0baf1> in <module>() ----> 1 next(G) StopIteration:
生成器是可迭代的物件,我們可以通過for迴圈來迭代它
In [11]: G = ( x*2 for x in range(5))
In [12]: for x in G:
...: print(x)
...:
0
2
4
6
8
方法2:使用yield
In [13]: def fib(times): ...: n = 0 ...: a, b = 0, 1 ...: while n < times: ...: yield b ...: a, b = b, a+b ...: n += 1 ...: return 'done' ...: ...: In [14]: F = fib(5) In [15]: next(F) Out[15]: 1 In [16]: next(F) Out[16]: 1 In [17]: next(F) Out[17]: 2 In [18]: next(F) Out[18]: 3 In [19]: next(F) Out[19]: 5 In [20]: next(F) --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-20-372178f5f53b> in <module>() ----> 1 next(F) StopIteration: done In [21]: for n in fib(5): ...: print(n) ...: 1 1 2 3 5
但是用for迴圈呼叫generator時,發現拿不到generator的ruturn的返回值。如果想要拿到返回值,必須捕獲StopIteration錯誤,返回值包含在StopIteration的value中。
In [22]: g = fib(5)
In [23]: while True:
...: try:
...: x = next(g)
...: print("value:%d"%x)
...: except StopIteration as e:
...: print("生成器返回值:%s"%e.value)
...: break
...:
value:1
value:1
value:2
value:3
value:5
生成器返回值:done
總結:生成器是這樣一個函式,它記住上一次返回時在函式體中的位置。對生成器函式的第二次(或第n次)呼叫跳轉至該函式中間,而上次呼叫的所有區域性變數都白痴不變。
生成器的特點:1、節約記憶體 2、迭代到下一次的呼叫時,所使用的引數都是第一次所保留下的,即是說,在整個所有函式呼叫的引數都是第一次所呼叫時保留的,而不是新建立的。
二、迭代器
迭代是訪問集合元素的一種方式。迭代器是一個可以記住遍歷的位置的物件。迭代器物件從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會後退。
1.可迭代物件
以直接作用於for迴圈的資料型別有以下幾種:
一類是集合資料型別,如list、tuple、dict、set、str等
一類是generator, 包括生成器和帶yield的generator function
這些可以直接作用於for迴圈的物件統稱為可迭代物件:Iterable。
2、判斷是否可以迭代
可以使用isinstance()判斷一個物件是否是Iterable物件:
In [24]: from collections import Iterable
In [25]: isinstance([],Iterable)
Out[25]: True
In [26]: isinstance({},Iterable)
Out[26]: True
In [27]: isinstance('abc',Iterable)
Out[27]: True
In [28]: isinstance(( x for x in range(10)), Iterable)
Out[28]: True
In [29]: isinstance(100, Iterable)
Out[29]: False
3、迭代器
可以被next()函式呼叫並不斷返回下一個值的物件稱為迭代器:Iterator,可以使用isinstance()判斷一個物件是否是Iterator物件。
In [30]: from collections import Iterator
In [31]: isinstance(( x for x in range(10)), Iterator)
Out[31]: True
In [32]: isinstance([], Iterator)
Out[32]: False
In [33]: isinstance({}, Iterator)
Out[33]: False
In [34]: isinstance('abc', Iterator)
Out[34]: False
In [35]: isinstance(100, Iterator)
Out[35]: False
In [36]: In [34]: isinstance('abc', Iterator)
...: Out[34]: False
...:
...: In [35]: isinstance(100, Iterator)
...: Out[35]: False
...:
4、iter()函式
生成器都是Iterator物件,但list、dict、str雖然是Iterable,卻不是Iterator(迭代器一定可以迭代,可迭代的不一定是迭代器)
如果把list、dict、str等Iterable變成Iterator可以使用iter()函式
In [37]: isinstance(iter([]), Iterator)
Out[37]: True
In [38]: isinstance(iter('abc'), Iterator)
Out[38]: True
總結:
凡是可作用於for迴圈的物件都是Iterable型別
凡是可以作用於next()函式的物件都是Iterator型別
集合資料型別如list、dict、str等是Iterable但不是Iterator,不過可以通過iter()函式獲得一個Iterator物件。
三、閉包
1、什麼是閉包
# 定義一個函式
def test(number):
# 在函式內部再定義一個函式,並且這個函式用到了外邊函式的變數,
# 那麼將這個函式以及用到的一些變數稱之為閉包
def test_in(number_in):
print("in test_in 函式,number_in is %d"%number_in)
return number+number_in
# 其實這裡返回的就是閉包的結果
return test_in
# 給test函式賦值,這個20就是給引數number
ret = test(20)
# 注意這裡的100其實給引數number_in
print(ret(100))
# 注意這裡的200其實給引數number_in
print(ret(200))
#result:
# in test_in 函式,number_in is 100
# 120
# in test_in 函式,number_in is 200
# 220
def line_conf(a, b):
def line(x):
return a*x + b
return line
line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5))
print(line2(5))
# result:
# 6
# 25
這樣就確定了函式的最終形式(y = x + 1 和 y = 4x + 5),閉包具有提高程式碼可複用性的作用。
四、裝飾器
#定義函式:完成包裹資料
def makeBold(fn):
def wrapped():
return "<b>" + fn() + "</b>"
return wrapped
#定義函式:完成包裹資料
def makeItalic(fn):
def wrapped():
return "<i>" + fn() + "</i>"
return wrapped
@makeBold
def test1():
return "hello world-1"
@makeItalic
def test2():
return "hello world-2"
@makeBold
@makeItalic
def test3():
return "hello world-3"
print(test1())
print(test2())
print(test3())
# result:
# <b>hello world-1</b>
# <i>hello world-2</i>
# <b><i>hello world-3</i></b>
from time import ctime, sleep
def timefun(func):
def wrappedfunc(*args, **kwargs):
print("%s called at %s"%(func.__name__, ctime()))
func(*args, **kwargs)
return wrappedfunc
@timefun
def foo(a, b, c):
print(a+b+c)
foo(3,5,7)
sleep(2)
foo(2,4,9)
#result:
# foo called at Mon Sep 10 21:04:21 2018
# 15
# foo called at Mon Sep 10 21:04:23 2018
# 15