python基礎(8)--迭代器、生成器、裝飾器

1.迭代器app

  迭代器是訪問集合元素的一種方式。迭代器對象從集合的第一個元素開始訪問,直到全部的元素被訪問完結束。迭代器只能往前不會後退,不過這也沒什麼,由於人們不多在迭代途中日後退。另外,迭代器的一大優勢是不要求事先準備好整個迭代過程當中全部的元素。迭代器僅僅在迭代到某個元素時才計算該元素,而在這以前或以後,元素能夠不存在或者被銷燬。這個特色使得它特別適合用於遍歷一些巨大的或是無限的集合,好比幾個G的文件異步

特色:ide

  1).訪問者不須要關心迭代器內部的結果,僅須要經過next()方法不斷去取下一個內容函數

  2).不能隨機訪問集合中的某個值,只能從頭至尾依次訪問測試

  3).訪問到一半時不能往回退spa

  4).便於循環比較大的數據集合,節省內存code

  Python內置函數iter就是一個簡單的幫助咱們創造一個迭代器的內置函數。對象

 1 names = iter(['a' , 'b', 'c'])
 2 print(names)
 3 print(names.__next__())
 4 print(names.__next__())
 5 print(names.__next__())
 6 print(names.__next__())
 7 
 8 #第一個輸出打印迭代器對象
 9 #第二三四次next方法每次都去獲取迭代對象的值,每次往下取一個值,直到取完
10 #第五次輸出    print(names.__next__())
11 #StopIteration,由於迭代器裏面的對象已經取完了,因此出現這個異常
View Code

  其實事實上,咱們不多會使用__next__()方法一個一個的去取值,多數狀況下都是使用for in循環進行遍歷blog

 

2.生成器內存

  一個函數調用時返回一個迭代器,這個函數就叫作生成器(generator)。若是函數中包含yield語法,那這個函數就會變成生成器

def fei(num):
    x, y, z = 0, 0, 1
    while x < num:
        if x == 0:  #當第一次循環時,返回數列0
            yield y #生成器返回一個迭代對象
        else:   #不然返回z
            yield z
            y, z = z, y + z
        x += 1

print(fei(9).__next__())    #只從迭代器裏取一次值


for i in fei(9):    #經過遍歷獲取迭代器內容
    print(i)

  生成器異步應用

import time
def consumer(name): #定義消費者函數
    print("%s 準備吃包子啦!" % name)  #打印消費者名字
    while True:
        baozi = yield   #當代碼運行到這裏時,返回一個迭代器對象,當對象第二次調用時,會接收值並賦值給baozi
        print("包子[%s]來了,被[%s]吃了!" % (baozi, name))  #打印當前使用的迭代器對象,及接收得值


def producer(*name):    #定義一個生產者函數,並使用動態參數
    if len(name) == 3:  #當傳入三個參數時,調用三次消費者函數
        c = consumer(name[0])
        c2 = consumer(name[1])
        c3 = consumer(name[2])
    c.__next__()    #經過函數內置方法獲取迭代器內部元素
    c2.__next__()
    c3.__next__()
    print("老子開始準備作包子啦!")
    for i in range(1, 11):
        time.sleep(1)
        print("作了%s個包子!" % len(name))
        c.send(i)   #經過迭代器方法的send屬性給生成器傳送值
        c2.send(i)
        c3.send(i)



producer('alex', 'tom', 'eric') #建立一個生產者對象,並傳入三個消費者名字



'''
執行結果
alex 準備吃包子啦!
tom 準備吃包子啦!
eric 準備吃包子啦!
老子開始準備作包子啦!
作了3個包子!
包子[1]來了,被[alex]吃了!
包子[1]來了,被[tom]吃了!
包子[1]來了,被[eric]吃了!
作了3個包子!
包子[2]來了,被[alex]吃了!
包子[2]來了,被[tom]吃了!
包子[2]來了,被[eric]吃了!
作了3個包子!
包子[3]來了,被[alex]吃了!
包子[3]來了,被[tom]吃了!
包子[3]來了,被[eric]吃了!
作了3個包子!
包子[4]來了,被[alex]吃了!
包子[4]來了,被[tom]吃了!
包子[4]來了,被[eric]吃了!
...

'''

 

3.裝飾器

  先來了解一下函數

def func():
    print('hello world')

print(func) #第一次輸出的是一個函數對象
print(type(func))   #第二次輸出的是一個類型爲函數
func()  #第三次纔是執行函數
print(func())   #第四次是執行函數後,並打印函數的返回結果,函數沒有指定返回內容,因此是默認返回值:None
print(type(func())) #第五次也是先執行函數,再打印返回值對象的類型,能夠看出,返回值對象類型是NoneType


'''
第一次
<function func at 0x0000000002D771E0>
第二次
<class 'function'>
第三次
hello world
第四次
hello world
None
第五次
hello world
<class 'NoneType'>

'''

  以上總結出,函數不加括號是沒有執行的,這時這個函數名只是一個函數對象,加括號後,函數將會執行函數體代碼,最終這個函數則是函數執行完成後返回的對象

  有了上面這個基礎,咱們再來看看裝飾器

def wrapper(func):
    print(func) #打印參數
    return func  #返回參數



@wrapper
def index():
    print('hello')


index()

#執行順序是,解釋器從上往下讀取代碼
#遇到函數時,只加載定義的函數對象,並不執行函數體代碼
#而後遇到裝飾器@wrapper時
#解釋器就會跳到這個wrapper函數
#而後執行這個wrapper函數內部代碼
#經過觀察可發現,這個wrapper函數必定會傳入一個參數,由於測試發現,不傳入一個參數,程序執行會拋出須要一個參數的異常錯誤
#經過分析這個參數,發現這個參數打印結果是一個函數對象
#而後wrapper函數體代碼執行完畢後,繼續往下執行,遇到函數index
#也是加載這個函數對象,並不執行內部函數體代碼
#當遇到代碼index()時,結合以前積累的函數基礎知識
#這個寫法實際是開始執行一個函數,因此解釋器會跳到指定的index函數對象
#而後開始執行這個函數代碼塊
#整個執行過程結束



#執行結果
#<function index at 0x0000000002D272F0>
#hello

  下面的列子具體說明裝飾器的工做原理

def wrapper(func):
    print(func) #打印參數
    #return func`#返回參數,如今註釋掉這個返回值

@wrapper
def index():
    print('hello')


print(type(index))  #加上一句輸出類型代碼語句
index()


#執行結果
#<function index at 0x0000000002D972F0>
#<class 'NoneType'>
#TypeError: 'NoneType' object is not callable

#首先分析下這個結果
#會不會很驚訝,只是針對上面的例子僅僅註釋掉一個返回值而已,代碼就不能工做了
#首先解釋器仍是從上往下讀取代碼
#遇到函數時,只加載定義函數對象,並不執行函數替代嗎
#而後遇到裝飾器@wrapper時
#解釋器會跳到這個wrapper函數
#而後執行這個wrapper函數內部代碼
#經過分析這個參數,發現這個參數打印結果是一個函數對象
#而後wrapper函數體代碼執行完畢後,繼續往下執行,遇到函數index
#也是隻加載函數對象,並不執行內部函數體代碼
#關鍵點來了
#代碼執行到打印對象類型語句時,結果倒是一個NoneType類型,根據以前對函數的基本介紹,這裏的類型應該是一個函數類型纔對呀
#爲何呢?命名定義了index函數,打印index類型倒是NoneType類型?
#咱們以前也看到只有函數沒有返回值時,函數默認會返回一個None對象,故而這個對象的類型也就是NoneType類型了
#僅僅是加了一個裝飾器代碼@wrapper,就出現這個狀況
#咱們上一個例子已經說明,這個裝飾器會攜帶一個參數,這個參數爲一個函數對象
#實際上,這時候這個裝飾器會對引用裝飾器的函數,也就是咱們這裏的index函數進行重構
#因此若是咱們不反悔一個函數對象時,name這個時候的index實質是一個普通的對象,不是函數類型了
#它已經被賦予None這個值了,而None不是一個函數對象,因此就沒有調用方法,就不能以括號方式執行
#這是解釋器督導index()這句代碼,依據以前的知識,都能看出這個是取執行index這個函數內部代碼快的語句
#可是執行時解釋卻拋了異常
#返回錯誤類型,TypeError: 'NoneType' object is not callable
#這個錯誤說明咱們index執行後,是不能被調用的,只有對象類型爲函數纔有內置調用方法
#由於這個index已經被重構,返回值已經變成了None,也就是說index對象目前僅僅是一個普通標識符,不是函數

  裝飾器的高級應用

  經過上面的示例能夠知道,只要代碼執行到裝飾器標識符,都會去執行裝飾器函數體,可是這個不是咱們想要的,咱們但願是隻有咱們調用引用裝飾器函數時,纔去執行這個裝飾器函數體,那怎麼辦呢?咱們知道,只有類型是函數對象時,代碼是不會被執行,只是加載到內存而已,那裝飾器函數體就能夠直接返回一個函數對象

  示例

def wrapper(func):
    def inner():
        print(func) #輸出是一個函數對象
        func()  #這裏實際是執行例子中原先定義的index函數對象的函數體
    return inner

@wrapper
def index():
    print('hello')


print(type(index))
index()


#這個例子知足了需求,當咱們不調用index函數時,獲得的僅僅是一個函數對象,並不會執行函數代碼
#當執行index函數,實際執行裝飾器函數體裏傳入的index函數,就是執行的它自己

  有參數的函數,或裝飾器實現傳參實例

#無參裝飾器,有參函數
def wrapper(func):
    def inner(name):    #這個參數最終會傳給這個函數體內部須要調用參數的對象
        func(name)  #這個參數個數是由原來函數,也就是index函數決定的
    return inner


@wrapper
def index(name):    #傳入一個參數
    print('hello %s' % name)


index('alex')
#無參裝飾器,多參函數
def wrapper(func):
    def inner(*args):   #使用動態參數
        func(*args)
    return inner

@wrapper
def index(*args):   #傳入一個參數
    print('hello %s' % ''.join(args))


index('alex', 'eric')
#無參裝飾器,多參函數2
def wrapper(func):
    def inner(*args, **kwargs): #使用動態參數
        func(*args, **kwargs)
    return inner

@wrapper
def index(*args, **kwargs):
    print('hello %s'% ' '.join(args))

index('alex', 'eric')
#有參裝飾器,多參函數
def one():
    print('one')

def two():
    print('two')

def func(arg1, arg2):
    def wrapper(oldfunc):
        def inner(*args, **kwargs):
            arg1()
            arg2()
            oldfunc(*args, **kwargs)
        return inner
    return wrapper

@func(one, two)
def index(*args, **kwargs):
    print('hello %s' % ''.join(args))

index('alex', 'eric')

#解釋器遇到裝飾器,因爲這個裝飾器是一個可執行函數
#故而先執行韓式,再次就成了咱們所認知的普通裝飾器了
#執行結果
#one
#two
#hello alexeric

  注:本篇博客轉載至博客園-曾春雲的筆記,再敲一遍是爲了加深印象

相關文章
相關標籤/搜索