最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(shí)間:2017年07月26日 08:35:29   投稿:jingxian  
下面小編就為大家?guī)?lái)一篇老生常談Python之裝飾器、迭代器和生成器。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

在學(xué)習(xí)python的時(shí)候,三大“名器”對(duì)沒(méi)有其他語(yǔ)言編程經(jīng)驗(yàn)的人來(lái)說(shuō),應(yīng)該算是一個(gè)小難點(diǎn),本次博客就博主自己對(duì)裝飾器、迭代器和生成器理解進(jìn)行解釋。

為什么要使用裝飾器

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

一個(gè)簡(jiǎn)單例子引入無(wú)參裝飾器

先來(lái)看簡(jiǎn)單的幾行代碼,代碼的運(yùn)行結(jié)果是先睡2秒,再打印"hello boy!":

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

我們現(xiàn)在我們需要為其添加一個(gè)程序計(jì)時(shí)功能,但是不能修改原始的代碼:

import time
def timmer(func):
 def wrapper():
  """計(jì)時(shí)功能"""
  time_start=time.time()
  func()
  time_end=time.time()
  print("Run time is %f "%(time_end-time_start))
 return wrapper
def foo():
 """打印"""
 time.sleep(2)
 print("Hello boy!")
foo=timmer(foo)
foo()
#運(yùn)行結(jié)果
Hello boy!
Run time is 2.000446 

看!我們沒(méi)有修改原來(lái)的代碼就實(shí)現(xiàn)了這個(gè)功能,因?yàn)楹瘮?shù)也是對(duì)象,所以能夠?qū)⒑瘮?shù)foo當(dāng)做參數(shù)傳遞給了函數(shù)timmer。

在python中,有個(gè)更簡(jiǎn)潔的方式來(lái)取代foo=timmer(foo),使用@timmer這種方式,這個(gè)在python中被稱為語(yǔ)法糖。

import time
def timmer(func):
 def wrapper():
  """計(jì)時(shí)功能"""
  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()

下面我們來(lái)一步一步的分析函數(shù)的執(zhí)行過(guò)程:

1.導(dǎo)入time模塊

import time

2.定義函數(shù)timmer,定義函數(shù)并不會(huì)執(zhí)行函數(shù)內(nèi)的代碼

def timmer(func):

3.調(diào)用裝飾器,相當(dāng)于foo=timer(foo),就是把函數(shù)foo作為參數(shù)穿給了函數(shù)timmer

@timmer

4.運(yùn)行函數(shù)timmer,接受了參數(shù) func=foo

def timmer(func):

5.在函數(shù)timmer內(nèi),定義了函數(shù)wrapper,wrapper函數(shù)內(nèi)部代碼也不執(zhí)行,然后將函數(shù)wrapper作為返回值返回

return wrapper

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

@timmer #等于 foo=timmer(foo)

7.運(yùn)行函數(shù)foo(),但是這里的函數(shù)已經(jīng)不是原來(lái)的那個(gè)函數(shù)了,可以打印foo,對(duì)的,因?yàn)橹拔覀儗rapper作為返回值傳給了foo,所以在這里執(zhí)行foo就是在執(zhí)行wrapper了,為了再確定這一點(diǎn)你也可打印wrapper,它們的內(nèi)存地址相同,所以都是指向同一個(gè)地址空間:

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

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

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

9.記錄結(jié)束時(shí)間,打印運(yùn)行時(shí)間,程序結(jié)束。

Hello boy!
Run time is 2.000161 
 

有參裝飾器

在前面的例子中,原函數(shù)沒(méi)有參數(shù),下面的來(lái)看一個(gè)當(dāng)原函數(shù)有參數(shù),該怎么修改裝飾器函數(shù)呢?

import time
def timmer(func):
 def wrapper(*args,**kwargs):
  """計(jì)時(shí)功能"""
  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):
 """返回兩個(gè)值的最大值"""
 res=x if x > y else y
 time.sleep(2)
 return res
res=my_max(1,2)
print(res)
#運(yùn)行結(jié)果
Run time is 2.000175

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

下面我們來(lái)看一個(gè)帶有參數(shù)的裝飾器:

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') #先先返回一個(gè)auth2 ==》@auth2 ==》 index=auth2(index) ==》 index=wrapper
def index():
 print("Welcome to China")
index()

如果裝飾器本身有參數(shù),就需要多一層內(nèi)嵌函數(shù),下面我們一步一步分析執(zhí)行流程:

1.定義函數(shù)auth

def auth(filetype):

2.調(diào)用解釋器,首先要運(yùn)行函數(shù)auth(filetype='file')

@auth(filetype='file')

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

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

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

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

5.當(dāng)運(yùn)行index,即運(yùn)行wrapper,運(yùn)行函數(shù)內(nèi)部代碼,filetype=="file",提示用戶輸出用戶名和密碼,判斷輸入是否正確,如果正確,則執(zhí)行函數(shù)func(),等于執(zhí)行原來(lái)的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.運(yùn)行結(jié)果測(cè)試

Please input your username:Frank
Please input your password:123456
Login successful
Welcome to China

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

import time
#
def timmer(func):
 def wrapper():
  """計(jì)時(shí)功能"""
  time_start=time.time()
  func()
  time_end=time.time()
  print("Run time is %f "%(time_end-time_start))
  # print("---",wrapper)
 return wrapper
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
@timmer
@auth(filetype='file') #先先返回一個(gè)auth2 ==》@auth2 ==》 index=auth2() ==》 index=wrapper
def index():
 print("Welcome to China")
index()

#測(cè)試結(jié)果
Please input your username:Frank
Please input your password:123456
Login successful
Welcome to China
Run time is 7.966267

注釋優(yōu)化

import time
def timmer(func):
 def wrapper():
  """計(jì)算程序運(yùn)行時(shí)間"""
  start_time=time.time()
  func()
  end_time=time.time()
  print("Run time is %s:"%(end_time-start_time))
 return wrapper
@timmer
def my_index():
 """打印歡迎"""
 time.sleep(1)
 print("Welcome to China!")
my_index()
print(my_index.__doc__)

#運(yùn)行結(jié)果
Welcome to China!
Run time is 1.0005640983581543:
計(jì)算程序運(yùn)行時(shí)間

當(dāng)我們使用了裝飾器的時(shí)候,雖然沒(méi)有修改代碼本身,但是在運(yùn)行的時(shí)候,比如上面這個(gè)例子,運(yùn)行my_index其實(shí)在運(yùn)行wrapper了,如果我們打印my_index的注釋信息,會(huì)打印wrapper()的注釋信息,那么該怎么優(yōu)化?

可以在模塊functools中導(dǎo)入wraps,具體見(jiàn)以下:

import time
from functools import wraps
def timmer(func):
 @wraps(func)
 def wrapper():
  """計(jì)算程序運(yùn)行時(shí)間"""
  start_time=time.time()
  func()
  end_time=time.time()
  print("Run time is %s:"%(end_time-start_time))
 return wrapper
@timmer
def my_index():
 """打印歡迎"""
 time.sleep(1)
 print("Welcome to China!")
my_index()
print(my_index.__doc__)
#運(yùn)行結(jié)果
Welcome to China!
Run time is 1.0003223419189453:
打印歡迎

這樣,在表面看來(lái),原函數(shù)沒(méi)有發(fā)生任何變化。

為什么要用迭代器

從字面意思,迭代就是重復(fù)反饋過(guò)程的活動(dòng),其目的通常是為了比較所需目標(biāo)或結(jié)果,在python中可以用迭代器來(lái)實(shí)現(xiàn),先來(lái)描述一下迭代器的優(yōu)缺點(diǎn),如果看不懂可以先略過(guò),等看完本博客再回頭看,相信你會(huì)理解其中的意思:

優(yōu)點(diǎn):

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

迭代器與列表相比,迭代器是惰性計(jì)算,更節(jié)省內(nèi)存

缺點(diǎn):

無(wú)法獲取迭代器的長(zhǎng)度,沒(méi)有列表靈活

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

什么是迭代器

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

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

下面我們來(lái)看一個(gè)簡(jiǎn)單的迭代器:

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

可以看到,使用內(nèi)置函數(shù)iter可以將列表轉(zhuǎn)換成一個(gè)列表迭代器,使用next()獲取值,一次值取一個(gè)值,當(dāng)值取完了,再使用一次next()的時(shí)候,會(huì)報(bào)異常StopIteration,可以通過(guò)異常處理的方式來(lái)避免,try-except-else就是一個(gè)最常用的異常處理結(jié)構(gòu):

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

查看可迭代對(duì)象和迭代器對(duì)象

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

from collections import Iterable
s="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))
#運(yùn)行結(jié)果
True
True
True
True
True
True

通過(guò)判斷,可以確定我們所知道的常用的數(shù)據(jù)類型都是可以被迭代的。

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

from collections import Iterable,Iterator
s="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))
#運(yùn)行結(jié)果
False
False
False
False
False
True

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

什么是生成器

生產(chǎn)器就是一個(gè)是帶有yield的函數(shù)

下面來(lái)看一個(gè)簡(jiǎn)單的生成器

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

生成器也是一個(gè)迭代器

from collections import Iterator
def my_yield():
 print('first')
 yield 1
g=my_yield()
print(isinstance(g,Iterator))
#運(yùn)行結(jié)果
True

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

print(next(g))
#運(yùn)行結(jié)果
first
1
 

生成器的執(zhí)行過(guò)程

我們來(lái)看以下下面這個(gè)例子,了解生產(chǎn)的執(zhí)行流程

def my_yield():
 print('first')
 yield 1
 print('second')
 yield 2
 print('Third')
 yield 3
g=my_yield()
next(g)
next(g)
next(g)
#運(yùn)行結(jié)果
first
second
Third

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

def my_yield():
g=my_yield()

2.開始第一次執(zhí)行next(),開始執(zhí)行生產(chǎn)器函數(shù) ,打印第一語(yǔ)句,遇到y(tǒng)ileld的時(shí)候暫停,并返回一個(gè)1,如果你想打印返回值的話,這里會(huì)顯示1

 print('first')
 yield 1

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

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

4.如果再加一次next()就會(huì)報(bào)出StopIteration異常了

生成器在每次暫停的時(shí)候,函數(shù)的狀態(tài)將被保存下來(lái),來(lái)看下面的例子:

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

for循環(huán)中隱含next(),每next一次,暫停一次,if語(yǔ)句判斷一次,然后執(zhí)行下一次next,可以看到我們的while循環(huán)并沒(méi)有無(wú)限循環(huán)下去,而是狀態(tài)被保存下來(lái)了。

協(xié)程函數(shù)

我們來(lái)看下面這個(gè)生成器和執(zhí)行結(jié)果

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送一個(gè)值,并繼續(xù)執(zhí)行代碼
e.send('tomato')
#運(yùn)行結(jié)果
Frank start to eat food
Frank get egg ,to start eat
Frank get tomato ,to start eat

send可直接以向yield傳值,含有yield表達(dá)式的函數(shù)我們也稱為協(xié)程函數(shù),

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

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

def init(func):
 def wrapper(*args):
  res = func(*args)
  next(res)  # 在這里執(zhí)行next
  return res
 return wrapper
@init
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')
e.send('egg') 
e.send('tomato')

所以在程序中有更多的生成器需要初始化的時(shí)候,直接調(diào)用這個(gè)裝飾器就可以了。

以上這篇老生常談Python之裝飾器、迭代器和生成器就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Python編程中對(duì)super函數(shù)的正確理解和用法解析

    Python編程中對(duì)super函數(shù)的正確理解和用法解析

    可能有人會(huì)想到,Python中既然可以直接通過(guò)父類名調(diào)用父類方法為什么還會(huì)存在super函數(shù)?其實(shí),很多人對(duì)Python中的super函數(shù)的認(rèn)識(shí)存在誤區(qū),本文我們就帶來(lái)在Python編程中對(duì)super函數(shù)的正確理解和用法解析
    2016-07-07
  • Python+OpenCV實(shí)現(xiàn)圖像融合的原理及代碼

    Python+OpenCV實(shí)現(xiàn)圖像融合的原理及代碼

    這篇文章主要介紹了Python+OpenCV實(shí)現(xiàn)圖像融合的原理及代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-12-12
  • python中_init_.py的作用

    python中_init_.py的作用

    __init__.py的作用是告訴Python這是一個(gè)包,并且可以包含初始化操作、控制子模塊導(dǎo)入、設(shè)置包級(jí)別變量和函數(shù)等,本文就來(lái)詳細(xì)的介紹一下_init_.py的作用,感興趣的可以了解一下
    2025-01-01
  • Python?Pygame實(shí)戰(zhàn)之五款童年經(jīng)典游戲合集

    Python?Pygame實(shí)戰(zhàn)之五款童年經(jīng)典游戲合集

    本文為大家總結(jié)了五款利用Python+Pygame實(shí)現(xiàn)的童年經(jīng)典游戲:推箱子、滑雪、八分音符醬、保衛(wèi)蘿卜和飛機(jī)大戰(zhàn),快跟隨小編一起學(xué)習(xí)一下
    2022-04-04
  • python中合并兩個(gè)文本文件并按照姓名首字母排序的例子

    python中合并兩個(gè)文本文件并按照姓名首字母排序的例子

    這篇文章主要介紹了python中合并兩個(gè)文本文件并按照姓名首字母排序的例子,需要的朋友可以參考下
    2014-04-04
  • Pygame做一期吃豆子游戲的示例代碼

    Pygame做一期吃豆子游戲的示例代碼

    這篇文章主要介紹了Pygame做一期吃豆子游戲的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • pychram中切換python版本方式(附圖文)

    pychram中切換python版本方式(附圖文)

    這篇文章主要介紹了如何在PyCharm中切換Python版本的步驟,包括查看現(xiàn)有版本、文件設(shè)置切換、選擇解釋器路徑以及應(yīng)用更改,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2024-12-12
  • python 調(diào)用win32pai 操作cmd的方法

    python 調(diào)用win32pai 操作cmd的方法

    下面小編就為大家?guī)?lái)一篇python 調(diào)用win32pai 操作cmd的方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-05-05
  • 簡(jiǎn)介Python設(shè)計(jì)模式中的代理模式與模板方法模式編程

    簡(jiǎn)介Python設(shè)計(jì)模式中的代理模式與模板方法模式編程

    這篇文章主要介紹了Python設(shè)計(jì)模式中的代理模式與模板方法模式編程,文中舉了兩個(gè)簡(jiǎn)單的代碼片段來(lái)說(shuō)明,需要的朋友可以參考下
    2016-02-02
  • TensorBoard 計(jì)算圖的可視化實(shí)現(xiàn)

    TensorBoard 計(jì)算圖的可視化實(shí)現(xiàn)

    今天小編就為大家分享一篇TensorBoard 計(jì)算圖的可視化實(shí)現(xiàn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-02-02

最新評(píng)論

小金县| 建湖县| 北宁市| 阿坝县| 沙雅县| 沂南县| 襄汾县| 吉林市| 丹凤县| 永川市| 金平| 吉隆县| 郎溪县| 马边| 富蕴县| 亳州市| 株洲市| 肇州县| 疏附县| 柳州市| 剑河县| 砚山县| 弥勒县| 银川市| 黎川县| 会昌县| 兴义市| 瓮安县| 城步| 达拉特旗| 固始县| 日喀则市| 芜湖县| 永康市| 裕民县| 田东县| 安国市| 台山市| 紫云| 西平县| 昆明市|