아래 편집기는 Python의 데코레이터, 반복자 및 생성기에 대한 진부한 표현을 제공합니다. 편집자님이 꽤 좋다고 생각하셔서 지금 공유하고 모두에게 참고용으로 드리고자 합니다. 에디터를 따라가서 살펴볼까요
파이썬을 배울 때, 세 가지 "유명한 도구"는 다른 언어 프로그래밍 경험이 없는 사람들에게는 작은 어려움으로 간주되어야 합니다. 이 블로그는 블로거 자신의 데코레이터에 대한 이해에 중점을 둘 것입니다. 반복자와 생성기에 대한 이해가 설명됩니다.
데코레이터를 사용하는 이유
데코레이터란 무엇인가요? "데코레이션(Decoration)"은 말 그대로 특정 건물을 특정 아이디어와 스타일에 따라 아름답게 꾸미는 행위를 의미한다. 소위 "디바이스(device)"란 파이썬의 경우, 원본 코드를 수정하지 않고도 사용할 수 있는 방식을 말한다. 예를 들어, 소프트웨어가 온라인 상태가 된 후에는 소스 코드나 호출 방식을 수정하지 않고 주기적으로 새 기능을 추가해야 합니다. 이를 달성하기 위해 코드를 작성할 때도 마찬가지입니다. 우리는 또한 나중에 확장성을 고려해야 합니다. Python의 데코레이터를 단계별로 살펴보겠습니다.
매개변수가 없는 데코레이터를 소개하는 간단한 예
먼저 몇 줄의 간단한 코드를 살펴보겠습니다. 코드의 결과는 2초 동안 대기한 다음 "hello boy!"를 인쇄하는 것입니다.
import time def foo(): """打印""" time.sleep(2) print("Hello boy!") foo()
us 이제 원본 코드를 수정하지 않고 프로그램 타이밍 기능을 추가해야 합니다.
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를 사용하여 foo=timmer(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 @timmer #等于 foo=timmer(foo) def foo(): """打印""" time.sleep(2) print("Hello boy!") foo()
함수의 실행 과정을 단계별로 분석해 보겠습니다.
1. 시간 모듈 가져오기
import time
2 함수를 정의하면 함수의 코드가 실행되지 않습니다.
def timmer(func):
3. 데코레이터를 호출하는 것은 foo=timer(foo)와 같습니다. 이는 foo 함수를 매개변수로 timmer
@timmer
4에 전달하는 것을 의미합니다. =foo
def timmer(func):
5. 함수 timmer에 함수 래퍼가 정의되어 있고 래퍼 함수 내부의 코드가 실행되지 않은 후 함수 래퍼가 반환 값
return wrapper
6으로 반환됩니다. 반환 값은 foo에 할당됩니다. 3단계 foo=timmer(foo)에서는
@timmer #等于 foo=timmer(foo)
7을 기억하세요. foo() 함수를 실행하세요. 하지만 여기의 함수는 더 이상 원래 함수가 아닙니다. 래퍼가 반환 값으로 foo에 전달되었기 때문에 foo를 인쇄할 수 있습니다. 따라서 여기서 foo를 실행하면 래퍼가 실행됩니다. 따라서 래퍼도 인쇄할 수 있습니다. 모두 동일한 주소 공간을 가리킵니다.
<function timmer.<locals>.wrapper at 0x00000180E0A8A950> #打印foo的结果 <function timmer.<locals>.wrapper at 0x000001F10AD8A950> #打印wrapper的结果 foo()
8. 함수 래퍼를 실행하고, 시작 시간을 기록하고, func 함수를 실행합니다. 4단계에서 func는 foo에 의해 값이 할당됩니다. 원래 함수 foo는 2초 동안 잠을 자고 문자열을 인쇄합니다.
time_start=time.time() time.sleep(2) print("Hello boy!")
9. 종료 시간을 기록하고 실행 시간을 인쇄하면 프로그램이 종료됩니다.
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
원래 함수가 매개변수를 전달해야 하는 경우 이 예에서는 my_max에 전달해야 하는 두 개의 위치가 있습니다. 래퍼에 두 개의 형식 매개변수만 추가하면 됩니다. 이 예에서는 가변 매개변수가 사용됩니다. (*args, **kwargs)도 가능합니다. 이는 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=wrapper def index(): print("Welcome to China") index()
def auth(filetype):2. (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 auth24.auth2(index), func=index에서 Execute와 동일하며, 함수 래퍼를 정의하고 반환합니다. 이때 index는 실제로 래퍼
와 같습니다.
def wrapper(*args,**kwargs): return wrapper5. index 실행, 즉 래퍼 실행 시 filetype=="file" 함수의 내부 코드를 실행하고 사용자 이름과 비밀번호를 출력하라는 메시지를 표시하고 입력이 올바른지 확인합니다. , 원본 인덱스를 실행하고 인쇄하는 것과 동일한 func() 함수를 실행합니다.
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: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 중국어 웹사이트의 기타 관련 기사를 참조하세요!