Python學習之路-Day4,python之路-day4

來源:互聯網
上載者:User

Python學習之路-Day4,python之路-day4

Python學習之路第4天

學習內容

1.迭代器與產生器

2.裝飾器

 

1.迭代器與產生器

迭代器

迭代是Python最強大的功能之一,是訪問集合元素的一種方式。

迭代器是一個可以記住遍曆的位置的對象。

迭代器對象從集合等第一個元素開始訪問,直到所有的元素被訪問結束,迭代器只能往前不會後退。

迭代器有兩個基本的方法:iter()next()

字串,列表或元組對象都可以用於建立迭代器。

迭代器的一大優點是不要求事先準備好整個迭代過程中所有的元素。迭代器僅僅在迭代到某個元素時才計算該元素,而在這之前或之後,元素可以不存在或者被銷毀。這個特點使得它特別適合用於遍曆一些巨大的或是無限的集合,比如幾個G的檔案

特點:

>>> 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)

執行以上程式,輸出結果如下:

1234

也可以使用 next() 函數:

import sys         # 引入 sys 模組list=[1,2,3,4]it = iter(list)    # 建立迭代器對象while True:    try:        print (next(it))    except StopIteration:        sys.exit()

執行以上程式,輸出結果如下:

1234

凡是能夠用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)...014916

一般我們建立一個generator後,基本上不會調用next(),而是通過for迴圈來迭代它,並且不需要關心StopIteration的錯誤。

有時候,推算元素的演算法很複雜,並不能通過清單產生器實現,而需要通過函數列印出來,譬如菲波那切數列。

#!/usr/bin/env pythondef fib(Max):    n = 0    a,b = 0,1    while n < Max:        print(b)        a,b = b,a+b        n = n + 1fib(5)結果:11235

在Python中,使用了yield的函數被稱為產生器(generator)。

跟普通函數不同的是,產生器是一個返回迭代器的函數,只能用於迭代操作,更簡單點理解產生器就是一個迭代器。

在調用產生器啟動並執行過程中,每次遇到yield時函數會暫停並儲存當前所有燈運行資訊,返回yield的值。並在下一次執行next()方法時從當前位置繼續運行。

#!/usr/bin/env pythondef fib(Max):    n = 0    a,b = 0,1    while n < Max:        yield b        a,b = b,a+b        n = n + 1g = fib(5)print(next(g))print(next(g))for i in g:    print(i)結果:11235

在函數的執行過程中,遇到return才會返回,但是變成了generator的函數,遇到了yield就會中斷,並且返回yield的參數。

那麼,遇到了yield就會中斷並返回,我們能不能通過yield傳一些參數進去呢?

#!/usr/bin/env pythondef fib(Max):    n = 0    a,b = 0,1    while n < Max:        e = yield b        print(e)        a,b = b,a+b        n = n + 1g = fib(5)print(next(g))print(next(g))print(g.send("the third element"))print(g.send("forth"))結果:1None1the third element2forth3

在上面的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,所以接受一個函數作為參數,並返回一個函數。我們要藉助Python的@文法,吧decorator置於函數的定義處:

@log  # 把這句放在這兒就相當於執行了 now = log(now)def now():    print("2017-05-01")

調用now()函數,不僅會運行now()函數本身,還會運行now()函數前列印一行日誌:

>>> now()call now():2017-05-01

把@log放在now()函數的定義處,就相當於執行了語句:

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 functoolsdef 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 functoolsdef 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()

 裝飾器小結:

在物件導向(OOP)的設計模式中,decorator被稱為裝飾模式。OOP的裝飾模式需要通過繼承和組合來實現,而Python除了能支援OOP的decorator外,直接從文法層次支援decorator(物件導向後面學到)。Python的decorator可以用函數實現,也可以用類實現。

decorator可以增強函數的功能,定義起來雖然有點複雜,但使用起來非常靈活和方便。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.