下面小編就為大家帶來一篇老生常談Python之裝飾器、迭代器和生成器。小編覺得蠻不錯的,現在就分享給大家,也給大家做個參考。一起跟著小編過來看看吧
在學習python的時候,三大「名器」對沒有其他語言程式設計經驗的人來說,應該算是一個小難點,本次部落格就部落客自己對裝飾器、迭代器和生成器理解進行解釋。
為什麼要使用裝飾器
什麼是裝飾器? 「裝飾」從字面意思來誰就是對特定的建築物內按照一定的思路和風格進行美化的一種行為,所謂「器」就是工具,對於python來說裝飾器就是能夠在不修改原始的代碼情況下為其添加新的功能,例如一款軟體上線之後,我們需要在不修改原始程式碼和不修改被調用的方式的情況下還能為期添加新的功能,在python種就可以用裝飾器來實現,同樣在寫程式碼的時候也要考慮到後面的可擴充性,下面我們來看一步一步的看一下python的裝飾器。
一個簡單範例引入無參裝飾者
#先來看簡單的幾行程式碼,程式碼的運作結果是先睡2秒,再列印"hello boy!":
import time def foo(): """打印""" time.sleep(2) print("Hello boy!") foo()
我們現在我們需要為其添加一個程式計時功能,但是不能修改原始的程式碼:
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)) return wrapper def 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 time def 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 time2.定義函數timmer,定義函數並不會執行函數內的程式碼
#
def timmer(func):3.呼叫裝飾器,相當於foo=timer(foo ),就是把函數foo當參數穿給了函數timmer
@timmer4.運行函數timmer,接受了參數func=foo
#
def timmer(func):5.在函數timmer內,定義了函數wrapper,wrapper函數內部程式碼也不執行,然後將函數wrapper當作回傳值回傳
##
return wrapper
@timmer #等于 foo=timmer(foo)
<function timmer.<locals>.wrapper at 0x00000180E0A8A950> #打印foo的结果 <function timmer.<locals>.wrapper at 0x000001F10AD8A950> #打印wrapper的结果 foo()
time_start=time.time() time.sleep(2) print("Hello boy!")
Hello boy! Run time is 2.000161
有參考裝飾器在前面的例子中,原始函數沒有參數,下面的來看一個當原函數有參數,該怎麼修改裝飾函數呢?
import time def 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 res res=my_max(1,2) print(res) #运行结果 Run time is 2.000175
下面我們來看一個
有參數的裝飾器:
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=wrapper def index(): print("Welcome to China") index()
1.定義函數auth
#
def auth(filetype):
@auth(filetype='file')
def auth(filetype): def auth2(func): def wrapper(*args,**kwargs): return wrapper return auth2
def wrapper(*args,**kwargs): return wrapper
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()
Please input your username:Frank Please input your password:123456 Login successful Welcome 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 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') #先先返回一个auth2 ==》@auth2 ==》 index=auth2() ==》 index=wrapper def index(): print("Welcome to China") index() #测试结果 Please input your username:Frank Please input your password:123456 Login successful Welcome to China Run time is 7.966267
注释优化
import time def timmer(func): def wrapper(): """计算程序运行时间""" 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__) #运行结果 Welcome to China! Run time is 1.0005640983581543: 计算程序运行时间
当我们使用了装饰器的时候,虽然没有修改代码本身,但是在运行的时候,比如上面这个例子,运行my_index其实在运行wrapper了,如果我们打印my_index的注释信息,会打印wrapper()的注释信息,那么该怎么优化?
可以在模块functools中导入wraps,具体见以下:
import time from functools import wraps def 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 @timmer def 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 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)) #运行结果 True True True True True True
通过判断,可以确定我们所知道的常用的数据类型都是可以被迭代的。
使用Iterator模块可以判断对象是否是迭代器:
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)) #运行结果 False False False False False True
可知只有文件是迭代器,所以可以直接使用next(),而不需要转换成迭代器。
什么是生成器
生产器就是一个是带有yield的函数
下面来看一个简单的生成器
def my_yield(): print('first') yield 1 g=my_yield() print(g) #运行结果 <generator object my_yield at 0x0000024366D7E258>
生成器也是一个迭代器
from collections import Iterator def my_yield(): print('first') yield 1 g=my_yield() print(isinstance(g,Iterator)) #运行结果 True
那就可以用next()来取值了
print(next(g)) #运行结果 first 1
生成器的执行过程
我们来看以下下面这个例子,了解生产的执行流程
def my_yield(): print('first') yield 1 print('second') yield 2 print('Third') yield 3 g=my_yield() next(g) next(g) next(g) #运行结果 first second Third
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+=1 g=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 food Frank get egg ,to start eat Frank 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 @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')
所以在程序中有更多的生成器需要初始化的时候,直接调用这个装饰器就可以了。
以上是Python中關於裝飾器與迭代器以及生成器的實例詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!