国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Python > 正文

老生常談Python之裝飾器、迭代器和生成器

2020-01-04 17:04:46
字體:
來源:轉載
供稿:網友

在學習python的時候,三大“名器”對沒有其他語言編程經驗的人來說,應該算是一個小難點,本次博客就博主自己對裝飾器、迭代器和生成器理解進行解釋。

為什么要使用裝飾器

什么是裝飾器?“裝飾”從字面意思來誰就是對特定的建筑物內按照一定的思路和風格進行美化的一種行為,所謂“器”就是工具,對于python來說裝飾器就是能夠在不修改原始的代碼情況下給其添加新的功能,比如一款軟件上線之后,我們需要在不修改源代碼和不修改被調用的方式的情況下還能為期添加新的功能,在python種就可以用裝飾器來實現,同樣在寫代碼的時候也要考慮到后面的可擴展性,下面我們來看一步一步的看一下python的裝飾器。

一個簡單例子引入無參裝飾器

先來看簡單的幾行代碼,代碼的運行結果是先睡2秒,再打印"hello boy!":

import timedef foo(): """打印""" time.sleep(2) print("Hello boy!")foo()

我們現在我們需要為其添加一個程序計時功能,但是不能修改原始的代碼:

import timedef timmer(func): def wrapper():  """計時功能"""  time_start=time.time()  func()  time_end=time.time()  print("Run time is %f "%(time_end-time_start)) return wrapperdef foo(): """打印""" time.sleep(2) print("Hello boy!")foo=timmer(foo)foo()#運行結果Hello boy!Run time is 2.000446 

看!我們沒有修改原來的代碼就實現了這個功能,因為函數也是對象,所以能夠將函數foo當做參數傳遞給了函數timmer。

在python中,有個更簡潔的方式來取代foo=timmer(foo),使用@timmer這種方式,這個在python中被稱為語法糖。

import timedef timmer(func): def wrapper():  """計時功能"""  time_start=time.time()  func()  time_end=time.time()  print("Run time is %f "%(time_end-time_start)) return wrapper@timmer  #等于 foo=timmer(foo)def foo(): """打印""" time.sleep(2) print("Hello boy!")foo()

下面我們來一步一步的分析函數的執行過程:

1.導入time模塊

import time

2.定義函數timmer,定義函數并不會執行函數內的代碼

def timmer(func):

3.調用裝飾器,相當于foo=timer(foo),就是把函數foo作為參數穿給了函數timmer

@timmer

4.運行函數timmer,接受了參數 func=foo

def timmer(func):

5.在函數timmer內,定義了函數wrapper,wrapper函數內部代碼也不執行,然后將函數wrapper作為返回值返回

return wrapper

6.將返回值賦值給了foo,在第3步中,foo=timmer(foo),還記吧

@timmer #等于 foo=timmer(foo)

7.運行函數foo(),但是這里的函數已經不是原來的那個函數了,可以打印foo,對的,因為之前我們將wrapper作為返回值傳給了foo,所以在這里執行foo就是在執行wrapper了,為了再確定這一點你也可打印wrapper,它們的內存地址相同,所以都是指向同一個地址空間:

<function timmer.<locals>.wrapper at 0x00000180E0A8A950> #打印foo的結果<function timmer.<locals>.wrapper at 0x000001F10AD8A950> #打印wrapper的結果foo()

8.運行函數wrapper,記錄開始時間,執行函數func,在第4步的時候,func被foo賦值,運行func就是在運行原函數foo,睡2秒,打印字符串;

time_start=time.time() time.sleep(2) print("Hello boy!")

9.記錄結束時間,打印運行時間,程序結束。

Hello boy!Run time is 2.000161  

有參裝飾器

在前面的例子中,原函數沒有參數,下面的來看一個當原函數有參數,該怎么修改裝飾器函數呢?

import timedef timmer(func): def wrapper(*args,**kwargs):  """計時功能"""  start_time=time.time()  res=func(*args,**kwargs)  end_time=time.time()  print("Run time is %f"%(end_time-start_time))  return res return wrapper@timmer def my_max(x,y): """返回兩個值的最大值""" res=x if x > y else y time.sleep(2) return resres=my_max(1,2)print(res)#運行結果Run time is 2.000175

當原函數有需要傳入參數的時候,在這個例子my_max有兩個位置形成需要傳入參數,只需要在wrapper上添加兩個形參,本例子中使用了可變參數(*args,**kwargs)也是可以的,這是@timmer就等于my_max(1,2)=timmer(my_max)

下面我們來看一個帶有參數的裝飾器:

def auth(filetype): def auth2(func):  def wrapper(*args,**kwargs):   if filetype == "file":    username=input("Please input your username:")    passwd=input("Please input your password:")    if passwd == '123456' and username == 'Frank':     print("Login successful")     func()    else:     print("login error!")   if filetype == 'SQL':    print("No SQL")  return wrapper return auth2@auth(filetype='file') #先先返回一個auth2 ==》@auth2 ==》 index=auth2(index) ==》 index=wrapperdef index(): print("Welcome to China")index()

如果裝飾器本身有參數,就需要多一層內嵌函數,下面我們一步一步分析執行流程:

1.定義函數auth

def auth(filetype):

2.調用解釋器,首先要運行函數auth(filetype='file')

@auth(filetype='file')

3.運行函數auth,定義了一個函數auth2,并作為返回值返回,那么這個@auth(filetype='file')就等同于@auth2,等同于index=auth2(index)

def auth(filetype): def auth2(func):  def wrapper(*args,**kwargs):  return wrapper return auth2

4.auth2(index)執行,func=index,定義函數wrapper,并返回之,這時候index其實就是等于wrapper了

def wrapper(*args,**kwargs):return wrapper

5.當運行index,即運行wrapper,運行函數內部代碼,filetype=="file",提示用戶輸出用戶名和密碼,判斷輸入是否正確,如果正確,則執行函數func(),等于執行原來的index,打印

if filetype == "file":    username=input("Please input your username:")    passwd=input("Please input your password:")    if passwd == '123456' and username == 'Frank':     print("Login successful")     func()

6.運行結果測試

Please input your username:FrankPlease input your password:123456Login successfulWelcome to China

裝飾器也是可以被疊加的:

import time#def timmer(func): def wrapper():  """計時功能"""  time_start=time.time()  func()  time_end=time.time()  print("Run time is %f "%(time_end-time_start))  # print("---",wrapper) return wrapperdef auth(filetype): def auth2(func):  def wrapper(*args,**kwargs):   if filetype == "file":    username=input("Please input your username:")    passwd=input("Please input your password:")    if passwd == '123456' and username == 'Frank':     print("Login successful")     func()    else:     print("login error!")   if filetype == 'SQL':    print("No SQL")  return wrapper return auth2@timmer@auth(filetype='file') #先先返回一個auth2 ==》@auth2 ==》 index=auth2() ==》 index=wrapperdef index(): print("Welcome to China")index()#測試結果Please input your username:FrankPlease input your password:123456Login successfulWelcome to ChinaRun time is 7.966267

注釋優化

import timedef timmer(func): def wrapper():  """計算程序運行時間"""  start_time=time.time()  func()  end_time=time.time()  print("Run time is %s:"%(end_time-start_time)) return wrapper@timmerdef my_index(): """打印歡迎""" time.sleep(1) print("Welcome to China!")my_index()print(my_index.__doc__)#運行結果Welcome to China!Run time is 1.0005640983581543:計算程序運行時間

當我們使用了裝飾器的時候,雖然沒有修改代碼本身,但是在運行的時候,比如上面這個例子,運行my_index其實在運行wrapper了,如果我們打印my_index的注釋信息,會打印wrapper()的注釋信息,那么該怎么優化?

可以在模塊functools中導入wraps,具體見以下:

import timefrom functools import wrapsdef timmer(func): @wraps(func) def wrapper():  """計算程序運行時間"""  start_time=time.time()  func()  end_time=time.time()  print("Run time is %s:"%(end_time-start_time)) return wrapper@timmerdef my_index(): """打印歡迎""" time.sleep(1) print("Welcome to China!")my_index()print(my_index.__doc__)#運行結果Welcome to China!Run time is 1.0003223419189453:打印歡迎

這樣,在表面看來,原函數沒有發生任何變化。

為什么要用迭代器

從字面意思,迭代就是重復反饋過程的活動,其目的通常是為了比較所需目標或結果,在python中可以用迭代器來實現,先來描述一下迭代器的優缺點,如果看不懂可以先略過,等看完本博客再回頭看,相信你會理解其中的意思:

優點:

迭代器在取值的時候是不依賴于索引的,這樣就可以遍歷那些沒有索引的對象,比如字典和文件

迭代器與列表相比,迭代器是惰性計算,更節省內存

缺點:

無法獲取迭代器的長度,沒有列表靈活

只能往后取值,不能倒著取值

什么是迭代器

那么在python什么才算是迭代器呢?

只要對象有__iter__(),那么它就是可迭代的,迭代器可以使用函數next()來取值

下面我們來看一個簡單的迭代器:

my_list=[1,2,3]li=iter(my_list)  #li=my_list.__iter__()print(li)print(next(li))print(next(li))print(next(li))#運行結果<list_iterator object at 0x000002591652C470>2

可以看到,使用內置函數iter可以將列表轉換成一個列表迭代器,使用next()獲取值,一次值取一個值,當值取完了,再使用一次next()的時候,會報異常StopIteration,可以通過異常處理的方式來避免,try-except-else就是一個最常用的異常處理結構:

my_list=[1,2,3]li=iter(my_list)while True: try:  print(next(li)) except StopIteration:  print("Over")  break else:  print("get!")#運行結果get!get!get!Over

查看可迭代對象和迭代器對象

使用Iterable模塊可以判斷對象是否是可迭代的:

from collections import Iterables="hello" #定義字符串l=[1,2,3,4] #定義列表t=(1,2,3) #定義元組d={'a':1} #定義字典set1={1,2,3,4} #定義集合f=open("a.txt") #定義文本# 查看是否都是可迭代的print(isinstance(s,Iterable))print(isinstance(l,Iterable))print(isinstance(t,Iterable))print(isinstance(d,Iterable))print(isinstance(set1,Iterable))print(isinstance(f,Iterable))#運行結果TrueTrueTrueTrueTrueTrue

通過判斷,可以確定我們所知道的常用的數據類型都是可以被迭代的。

使用Iterator模塊可以判斷對象是否是迭代器:

from collections import Iterable,Iterators="hello"l=[1,2,3,4]t=(1,2,3)d={'a':1}set1={1,2,3,4}f=open("a.txt")# 查看是否都是可迭代的print(isinstance(s,Iterator))print(isinstance(l,Iterator))print(isinstance(t,Iterator))print(isinstance(d,Iterator))print(isinstance(set1,Iterator))print(isinstance(f,Iterator))#運行結果FalseFalseFalseFalseFalseTrue

可知只有文件是迭代器,所以可以直接使用next(),而不需要轉換成迭代器。

什么是生成器

生產器就是一個是帶有yield的函數

下面來看一個簡單的生成器

def my_yield(): print('first') yield 1g=my_yield()print(g)#運行結果<generator object my_yield at 0x0000024366D7E258>

生成器也是一個迭代器

from collections import Iteratordef my_yield(): print('first') yield 1g=my_yield()print(isinstance(g,Iterator))#運行結果True

那就可以用next()來取值了

print(next(g))#運行結果first1 

生成器的執行過程

我們來看以下下面這個例子,了解生產的執行流程

def my_yield(): print('first') yield 1 print('second') yield 2 print('Third') yield 3g=my_yield()next(g)next(g)next(g)#運行結果firstsecondThird

1.定義生成器my_yield,并將其賦值給了g

def my_yield():g=my_yield()

2.開始第一次執行next(),開始執行生產器函數 ,打印第一語句,遇到yileld的時候暫停,并返回一個1,如果你想打印返回值的話,這里會顯示1

 print('first') yield 1

3.再執行2次,打印字符串(每執行一次都會暫停一下)

 print('second') yield 2 print('Third') yield 3

4.如果再加一次next()就會報出StopIteration異常了

生成器在每次暫停的時候,函數的狀態將被保存下來,來看下面的例子:

def foo(): i=0 while True:  yield i  i+=1g=foo()for num in g: if num < 10:  print(num) else:  break#運行結果

for循環中隱含next(),每next一次,暫停一次,if語句判斷一次,然后執行下一次next,可以看到我們的while循環并沒有無限循環下去,而是狀態被保存下來了。

協程函數

我們來看下面這個生成器和執行結果

def eater(name): print('%s start to eat food'%name) while True:  food=yield  print('%s get %s ,to start eat'%(name,food)) print('done')e=eater('Frank')next(e)e.send('egg') #給yield送一個值,并繼續執行代碼e.send('tomato')#運行結果Frank start to eat foodFrank get egg ,to start eatFrank get tomato ,to start eat

send可直接以向yield傳值,含有yield表達式的函數我們也稱為協程函數,

這運行程序的時候,不可以直接send,必須先使用next()初始化生成器。

如果存在多個這樣的函數,那么我們每次執行的時候都要去next()一下,為了防止忘記這一步操作,可以使用裝飾器初始化:

def init(func): def wrapper(*args):  res = func(*args)  next(res)  # 在這里執行next  return res return wrapper@initdef eater(name): print('%s start to eat food'%name) while True:  food=yield  print('%s get %s ,to start eat'%(name,food)) print('done')e=eater('Frank')e.send('egg') e.send('tomato')

所以在程序中有更多的生成器需要初始化的時候,直接調用這個裝飾器就可以了。

以上這篇老生常談Python之裝飾器、迭代器和生成器就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持VEVB武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 本溪市| 胶南市| 平利县| 凤庆县| 营口市| 万宁市| 右玉县| 南开区| 宜兰市| 东城区| 保亭| 黔东| 宿松县| 松溪县| 贵南县| 聂拉木县| 牡丹江市| 维西| 南雄市| 新巴尔虎左旗| 攀枝花市| 万年县| 长宁县| 且末县| 武邑县| 蛟河市| 福清市| 宣威市| 桂阳县| 老河口市| 黄龙县| 龙陵县| 清远市| 旌德县| 安康市| 鄂伦春自治旗| 苗栗县| 南京市| 大邑县| 巴林右旗| 盐边县|