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

python - 閉包,裝飾器,迭代器,生成器

  在瞭解裝飾器之前,我們必須瞭解閉包是什麼?

  如果在一個函式中又定義一個函式,那麼又定義的這個函式被稱為內部函式,而原本的函式被稱為外部函式。

  而閉包的概念就是在外部函式中定義了一個內部函式,並且在內部函式中呼叫了外部函式的變數,最後外部函式的返回值是內部函式的引用。

閉包例子:

 1 #閉包的例項
 2 # outer是外部函式 a和b都是外函式的臨時變數
 3 def outer( a ):
 4     b = 10
 5     # inner是內部函式
 6     def inner():
 7         #在內部函式中 用到了外部函式的臨時變數
8 print(a+b) 9 # 外部函式的返回值是內部函式的引用 10 return inner 11 12 if __name__ == '__main__': 13 func = outer(1) 14 # 在func變數中是outer的返回值即inner函式的引用 15 func() # 結果11 16 # 呼叫了inner函式輸出結果為11
閉包簡單例子

  使用閉包的過程中,一旦外部函式被呼叫一次,返回了內部函式的引用,雖然每次呼叫內部函式,是開啟一個函式執行,過後消亡,但是閉包變數實際上只有一份,每次開啟內部函式都在使用同一份閉包變數。

1 def outer(a):
2     def inner(b):
3         nonlocal a
4         a += b
5         return a
6     return inner
7 o = outer(10)
8 print(o(1))
9 print(o(3))
閉包中外部變數共享

   閉包的概念已經知道了,那麼什麼是裝飾器呢?

  簡單的來說裝飾器就是對原有的函式根據需要新增新的功能。下面舉一個簡單的例子:

原有函式

def printinfo():
    print('你好')

如果需要計算該函式的執行時間在不改變原有程式碼的基礎上實現

import time
def time_sum (printinfo):
    def inner():
        start_time = time.time()
        printinfo()
        time.sleep(1)
        end_time = time.time()-start_time
        return end_time
    return inner

@time_sum
def printinfo():
    print('你好')

a = printinfo()
print(a)

上面的例子是沒有引數的,為了使這個程式具有通用性可以對其進行優化,利用不定長引數*args,**kwargs,使得程式能夠接受任意引數。

1 def time_sum (func):
2     def inner(*args,**kwargs):
3         start_time = time.time()
4         func(*args,**kwargs)
5         time.sleep(1)
6         end_time = time.time()-start_time
7         return end_time
8     return inner
優化後的裝飾器程式碼

  多個裝飾器執行的順序就是從最後一個裝飾器開始,執行到第一個裝飾器,再執行函式本身。

列表生成式

  列表生成式,是Python內建的一種強大的生成list的表示式。

 

1 list1 = [i for i in range(0,101) if i%2 == 0]
2 print(list1)
輸出0-100之間的偶數

 

list1 = [i*i for i in range(0,7)]
print(list1)
輸出0-6之間的整數的平方

  通過列表生成式,我們可以直接建立一個列表。但是,受到記憶體限制,列表容量肯定是有限的。而且,建立一個包含100萬個元素的列表,不僅佔用很大的儲存空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。

  在Python中,一邊迴圈一邊計算的機制,稱為生成器:generator。

生成器

  要建立一個簡單的generator,只要把一個列表生成式的[]改成(),就建立了一個generator,但是對於生成器我們可以利用next()或取到下一個元素。但是基本上永遠不會呼叫next(),而是通過for迴圈來迭代它。

g = (i for i in range(101))
for i in g:
    print(i)

  還有一種方法建立生成器。如果一個函式定義中包含yield關鍵字,那麼這個函式就不再是一個普通函式,而是一個generator。

 1 def fab(max): 
 2     n, a, b = 0, 0, 1 
 3     while n < max: 
 4         yield b      # 使用 yield
 5         # print b 
 6         a, b = b, a + b 
 7         n = n + 1
 8  
 9 for n in fab(5): 
10     print n
斐波那契數列
 1 def triangles():
 2     N=[1]
 3     while True:
 4         yield N    
 5         N.append(0)
 6         N=[N[i-1] + N[i] for i in range(len(N))] 
 7  
 8 if __name__ == '__main__':
 9     n=0
10     for t in triangles():
11         print(t)
12         n=n+1
13         if n == 10:
14             break
楊輝三角

 

迭代器

  表示資料流的物件。重複呼叫迭代器的 __next__()方法(或將其傳遞給內建函式 next())返回流中的連續項。當沒有更多資料可用時,StopIteration會引發異常。此時,迭代器物件已耗盡,並且對其__next__()方法的任何進一步呼叫 StopIteration再次引發迭代器需要有一個__iter__()返回迭代器物件本身方法,因此每個迭代器也是可迭代的,並且可以在大多數接受其他迭代的地方使用。一個值得注意的例外是嘗試多次迭代傳遞的程式碼。list每次將容器物件(例如a )傳遞給iter()函式或函式中使用它時,它都會生成一個全新的迭代器 for環。使用迭代器嘗試此操作只會返回上一次迭代過程中使用的相同耗盡的迭代器物件,使其看起來像一個空容器。 

  凡是可作用於for迴圈的物件都是Iterable型別;

  凡是可作用於next()函式的物件都是Iterator型別,它們表示一個惰性計算的序列;

  集合資料型別如listdictstr等是Iterable但不是Iterator,不過可以通過iter()函式獲得一個Iterator物件。