python迭代器、生成器、装饰器
1 迭代器
这里我们先来回顾一下什么是可迭代对象(Iterable)?
可以直接作用于for循环的对象统称为可迭代对象,即Iterable。
# 一是集合数据类型,如list、tuple、dict、set、str等;
# 二是generator,包括生成器和带yield的generator function。
那么什么又是迭代器(Iterator)?
可以被next()函数调用并不断返回下一个值(直到没有数据时抛出StopIteration错误)的对象称为迭代器,即Iterator。
1 import collections 2 print(isinstance([], collections.Iterable)) # True 3 print(isinstance(iter([]), collections.Iterator)) # True 4 print(isinstance(iter([]), collections.Iterable)) # True 5 print(isinstance([], collections.Iterator)) # False 6 print(isinstance((x * x for x in range(10)), collections.Iterable))
# isinstance() 是python内建函数,返回对象是否是类或其子类的实例。若是,返回True,反之返回False。 # Iterable 英文是‘可迭代的’,形容词;Iterator英文是‘迭代器’,名词。 # 那么当 isinstance()的第二个参数是collections.Iterable时,是判断第一个参数是不是Iterable对象(可迭代对象) # 当 isinstance()的第二个参数是collections.Iterator时,是判断第一个参数是不是Iterator对象(迭代器对象) # 那么什么是可迭代对象?可以直接作用于for循环的对象统称为可迭代对象,即Iterable。 # 一是集合数据类型,如list、tuple、dict、set、str等; # 二是generator,包括生成器和带yield的generator function。 # 是么是迭代器?可以被next()函数调用并不断返回下一个值(直到没有数据时抛出StopIteration错误)的对象称为迭代器:Iterator。 # 你可能会问,为什么list、dict、str等数据类型不是Iterator? # 这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。 # 可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据, # 所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。 # Iterable 可以通过iter()函数转换得到 Iterator,但Iterable不一定是Iterator;而Iterator可以直接作用于for循环,所以Iterator是Iterable。
2 生成器
首先先理清几个概念:
generator
: A function which returns a generator iterator. It looks like a normal function except that it contains yield expressions for producing a series of values usable in a for-loop or that can be retrieved one at a time with the next() function.
generator iterator
: An object created by a generator funcion.
generator expression
: An expression that returns an iterator.
可见,我们常说的生成器,
就是带有 yield
的函数,而 generator iterator
则是 generator function
的返回值,即一个 generator
对象;
形如 (elem for elem in [1, 2, 3])
的表达式,称为 generator expression
,实际使用与 generator
无异。
Python’s generators provide a convenient way to implement the iterator protocol.
也就是说: generator
就是 iterator
的一种,以更优雅的方式实现的 iterator
。我们来一个例子:
1 from collections import Iterable 2 from collections import Iterator 3 from collections import Generator 4 5 def odd(): 6 print('step 1') 7 yield 1 8 print('step 2') 9 yield(3) 10 print('step 3') 11 yield(5) 12 13 ge = odd() 14 print(isinstance(ge, Iterator)) 15 print(isinstance(ge, Iterable)) 16 print(isinstance(ge, Generator)) 17 print(type(ge)) 18 # 结果 19 # True 20 # True 21 # True 22 # <class 'generator'>
这也充分印证了上面的说法,generator就是一种iterator。而且Gennerator这个类是继承了Iterator的。
3 装饰器
什么是装饰器(Decorator)?
本质上:是一个返回函数的高阶函数。
生产上,什么时候用装饰器?
当我们想要给一个函数func()增加某些功能,但又不希望修改func()
函数的源代码的时候就需要用装饰器了。(在代码运行期间动态增加功能)
假如,你有一个网站,之前是免费开放的,谁都可以访问。但是有一天你不想免费开放了,你想让大家必须登陆后才能访问,但是呢,网站已经上线了,一直是跑着的,不能修改源码。这个时候就要用这个装饰器了。
前奏:
假设你原先的网站首页是这个函数:
def home(): print("欢迎来到XX首页!") home()
首先我们必须得明白:函数也是一个对象(python里一切皆对象),且可以赋值给其他变量。例如:
def home(): print("欢迎来到XX首页!") f = home f()
这和直接调用home()结果是一样的。
那么怎么做到,不改变home的源码给它加上添加登录功能呢?看下面的代码,注意其中的讲解:
1 def login(func): 2 """ 3 在这里从新定义一个高阶函数, 4 这就是decorator。 5 我们一会儿会仔细分析。 6 """ 7 def wrapper(*args, **kwargs): 8 user = "zingp" # 假设这是数据库中的用户名和密码 9 passwd = "123" 10 username = input("输入用户名:") 11 password = input("输入密码:") 12 if username == user and password == passwd: 13 return func(*args, **kwargs) 14 else: 15 print("用户名或密码错误。") 16 return wrapper 17 18 19 @login # 利用python的@语法,把decorator置于home函数的定义处 相当于home = login(home) 20 def home(): 21 print("欢迎来到XX首页!") 22 23 home()
运行看看,是不是没改变home源码和home的调用方式,给home添加了登录验证的功能?
其实,这里@login相当于做一这么一件事:home = login(home)
那么当执行23行时就是这样的:login(home)()
login(home)是什么?他就是调用login这个函数后的返回值,即wrapper
此时,login(home)()即变成了 wrapper()
执行wrapper() ,返回home()函数并执行home()
整个过程就是这样。
但是这里还有个问题,就是当没加装饰器的时候print(home.__name__)得到的函数名是home,加了装饰器后print(home.__name__)得到的结果就是wrapper了。
我们虽然没有改home函数的源代码,但是更改了__name__属性,所以,最美好的结果是连属性也不更改吧?
你可能想写个wrapper.__name__ = func.__name__这样的代码,就可以了嘛。但是呢,Python内置的functools.wraps就是专门干这个事的。
请看完整的装饰器代码:
1 import functools # 先得导入这个工具 2 3 4 def login(func): 5 6 @functools.wraps(func) 7 def wrapper(*args, **kw): 8 user = "zingp" # 假设这是数据库中的用户名和密码 9 passwd = "123" 10 username = input("输入用户名:") 11 password = input("输入密码:") 12 if username == user and password == passwd: 13 return func(*args, **kw) 14 else: 15 print("用户名或密码错误。") 16 return wrapper 17 18 19 @login 20 def home(): 21 print("欢迎来到XX首页!") 22 23 home() 24 print(home.__name__)
现在是不是属性也没改了?
作者:ZingpLiu
出处:http://www.cnblogs.com/zingp/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
· 对象命名为何需要避免'-er'和'-or'后缀
· “你见过凌晨四点的洛杉矶吗?”--《我们为什么要睡觉》
· 编程神器Trae:当我用上后,才知道自己的创造力被低估了多少
· C# 从零开始使用Layui.Wpf库开发WPF客户端
· C#/.NET/.NET Core技术前沿周刊 | 第 31 期(2025年3.17-3.23)
· 开发的设计和重构,为开发效率服务