1. 程式人生 > >python-生成器、迭代器、閉包、裝飾器

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