Python学习笔记之生成器、迭代器和装饰器
这篇文章主要介绍 Python 中几个常用的高级特性,用好这几个特性可以让自己的代码更加 Pythonnic 哦
1、生成器
什么是生成器呢?简单来说,在 Python 中一边循环一边计算的机制称为 生成器(generator)
生成器最大的优点在于它支持延迟操作,所谓的延迟操作是指在需要的时候才进行运算产生结果
我们可以使用以下两种方法 创建生成器:
- 生成器表达式
类似于列表生成式,只需将列表生成式中的中括号替换成圆括号即可
>>> gen = (item for item in range(3))
>>> type(gen)
# <class 'generator'>
- 生成器函数
类似于普通函数,只需将普通函数中的 return 语句替换成 yield 语句即可
比较难以理解的的地方在于生成器函数和普通函数的执行流程不太一样
普通函数是顺序执行,遇到 return 语句则返回;
生成器函数在每次请求值时都执行一次代码,直至遇到 yield 或 return
yield 意味着返回一个值,且下次执行从上次返回的 yield 语句处开始,而 return 意味着生成器停止执行
>>> def counter(num):
temp = 0
while(temp < num):
temp += 1
yield temp
>>> gen = counter(3)
>>> type(gen)
# <class 'generator'>
我们可以使用以下三种方法 调用生成器:
- 内置方法:
__next__()
该方法返回调用生成器的运算结果,每次调用返回一个数据,直到调用结束
注意,生成器只能一直向前迭代,不能回退
>>> gen = (item for item in range(3))
>>> gen.__next__()
# 0
>>> gen.__next__()
# 1
>>> # 在 Python 中有一个全局方法 next(gen),作用与 gen.__next__() 类似
>>> next(gen)
# 2
>>> # 当生成器调用结束后,再次调用会抛出 StopIteration 异常
>>> next(gen)
# StopIteration
- 内置方法:
send()
该方法返回调用生成器的运算结果,同时向生成器内部发送数据
注意,在第一次调用 send() 函数前至少使用一次 next() 或 __next__() 方法
>>> def counter(num):
temp = 0
while(temp < num):
temp += 1
re = yield temp
print(re)
>>> gen = counter(3)
>>> next(gen)
# 1
>>> gen.send('Hello')
# Hello
# 2
>>> gen.send('World')
# World
# 3
>>> next(gen)
# None
# StopIteration
- for 循环
生成器也是一个可迭代对象,我们可以使用 for 循环进行遍历生成器中的每一个元素
需要注意的是,生成器只能遍历一次
>>> gen = (item for item in range(3))
>>> for item in gen:
print(item)
# 0
# 1
# 2
2、迭代器
我们首先来了解一下几个与迭代器相关的概念:
迭代(Iteration):在Python 中,我们可以通过 for 循环遍历 list 或 tuple 等数据类型,这种遍历我们称为迭代
可迭代对象(Iterable):可以直接作用于 for 循环的对象我们称为可迭代对象,一般包括集合数据类型和生成器
迭代器(Iterator):实现了迭代器协议的对象称为迭代器
那么如何判断可迭代对象和迭代器:
一个实现了 __iter__() 方法的对象是可迭代的,一个实现了 __iter__() 方法和 __next__() 方法的对象是迭代器
由此也可以看出,迭代器一定是可迭代对象,但可迭代对象不一定是迭代器
一个可作用于 for 循环的对象是可迭代的,一个可作用于 next() 方法的对象是迭代器
例如,集合数据类型是可迭代对象,但不是迭代器;生成器是可迭代对象,同时也是迭代器
>>> from collections import Iterable, Iterator
>>> li = [item for item in range(3)]
>>> isinstance(li,Iterable)
# True
>>> isinstance(li,Iterator)
# False
>>> gen = (item for item in range(3))
>>> isinstance(gen,Iterable)
# True
>>> isinstance(gen,Iterator)
# True
3、装饰器
(1)闭包
在了解装饰器之前,我们先来了解一下什么是闭包?先看看闭包的定义:
如果在一个内部函数中对外部作用域(但不是全局作用域)的变量进行引用,那么该内部函数称为闭包
>>> def external(x):
def internal(y):
return x+y
return internal
>>> func1 = external(5)
>>> func1(10)
# 15
>>> func2 = external(10)
>>> func2(10)
# 20
例如,在上面的代码中,internal 为内部函数,external 为外部函数
在内部函数 internal 中对外部作用域(但不是全局作用域)的变量 x 进行引用
那么这时我们可以称内部函数 internal 为闭包
(2)装饰器
装饰器实际上是一个闭包,它接受一个函数作为参数,返回一个经过装饰的函数
def decorator(func):
def wrapper(*args, **kwargs):
print('装饰器')
func()
print('装饰器')
return wrapper
@decorator
def func():
print('原有操作')
func()
# 装饰器
# 原有操作
# 装饰器
实际上,我们还可以为一个函数添加多个装饰器,注意观察它们之间的执行顺序:
def decorator1(func):
def wrapper(*args, **kwargs):
print('装饰器1')
func()
print('装饰器1')
return wrapper
def decorator2(func):
def wrapper(*args, **kwargs):
print('装饰器2')
func()
print('装饰器2')
return wrapper
@decorator1
@decorator2
def func():
print('原有操作')
func()
# 装饰器1
# 装饰器2
# 原有操作
# 装饰器2
# 装饰器1
装饰器常常用于日志功能,下面是一个例子:
from functools import wraps
def logger(func):
@wraps(func) # 添加 functools.wraps 可以防止原有函数自身的信息丢失
def wrapper(*args, **kwargs):
print(func.__name__ + ' is called')
return func(*args, **kwargs)
return wrapper
@logger
def func(x,y):
return x + y
res = func(3,4)
# func is called
装饰器还可以用于计时功能,下面是另外一个例子:
from functools import wraps
import time
def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs)
end_time = time.time()
print(end_time - start_time)
return wrapper
@timer
def func():
time.sleep(1)
func()
# 1.0003883838653564
【 阅读更多 Python 系列文章,请看 Python学习笔记 】
Python学习笔记之生成器、迭代器和装饰器的更多相关文章
- 流畅的python学习笔记第七章:装饰器
装饰器就如名字一样,对某样事物进行装饰过后然后返回一个新的事物.就好比一个毛坯房,经过装修后,变成了精装房,但是房子还是同样的房子,但是模样变了. 我们首先来看一个函数.加入我要求出函数的运行时间.一 ...
- Python学习笔记 (4) :迭代器、生成器、装饰器、递归、正则表达式等
迭代器 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退.另外,迭代器的一大优点是 ...
- 周末学习笔记——day02(带参装饰器,wraps修改文档注释,三元表达式,列表字典推导式,迭代器,生成器,枚举对象,递归)
一,复习 ''' 1.函数的参数:实参与形参 形参:定义函数()中出现的参数 实参:调用函数()中出现的参数 形参拿到实参的值,如果整体赋值(自己改变存放值的地址),实参不会改变,(可变类型)如果修改 ...
- python cookbook第三版学习笔记二十一:利用装饰器强制函数上的类型检查
在演示实际代码前,先说明我们的目标:能对函数参数类型进行断言,类似下面这样: @typeassert(int, int) ... def add(x, y): ... return x + y ...
- 老男孩python学习自修第十七天【装饰器】
装饰器:在某个方法执行前后去执行其他新定义的行为 例如: #!/usr/bin/env python # _*_ coding:UTF-8 _*_ def before_say_hello(): pr ...
- python学习 day12 (3月18日)----(装饰器内置函数)
读时间函数: # import time # def func(): # start_time = time.time() # 代码运行之前的时间 # print('这是一个func函数') # ti ...
- TypeScript学习笔记(九):装饰器(Decorators)
装饰器简介 装饰器(Decorators)为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式. 需要注意的是:装饰器是一项实验性特性,在未来的版本中可能会发生改变. 若要启用实验性的装饰器特 ...
- Python3学习笔记(十三):装饰器
装饰器就是一个闭包,它的主要作用是在不改变原函数的基础上对原函数功能进行扩展. 我们先来写一个简单的函数: from time import sleep def foo(): print(" ...
- python学习笔记之八:迭代器和生成器
一. 迭代器 在前面的笔记中,已经提到过迭代器(和可迭代),这里会对此进行深入讨论.只讨论一个特殊方法---__iter__,这个方法是迭代器规则的基础. 1.1 迭代器规则 迭代的意思是重复做一些事 ...
随机推荐
- 【Dairy】2016.10.17-1 OIer最悲剧的事情
OIer最悲剧的事情: 看完题,觉得很可做 然后开始码,码了很久 一测样例,不过.. 开始肉眼查错..手玩样例.. 过了很久 ...
- luogu 3951 小凯的疑惑
noip2017 D1T1 小凯的疑惑 某zz选手没有看出这道结论题,同时写出了exgcd却不会用,只能打一个哈希表骗了30分 题目大意: 两个互质的正整数a和b,求一个最小的正整数使这个数无法表示为 ...
- jsp中一行多条数据情况
1.实现效果:点击新增会在之前文本框后一直新增文本框并且保留新增的图片 效果图: 2.jsp代码: <table class="Business_Table"> < ...
- Python 常用算法记录
一.递归 汉诺塔算法:把A柱的盘子,移动到C柱上,最少需要移动几次,大盘子只能在小盘子下面 1.当盘子的个数为n时,移动的次数应等于2^n – 1 2.描述盘子从A到C: (1)如果A只有一个圆盘,可 ...
- 服务器端解决ajax跨域问题
这里描述以Tomcat为Web服务器情况下的解决办法,在Java Web程序的WEB-INF下的web.xml文件中加入如下配置即可. <!--cors filter--> <fil ...
- 对象的属性类型 和 VUE的数据双向绑定原理
如[[Configurable]] 被两对儿中括号 括起来的表示 不可直接访问他们 修改属性类型:使用Object.defineProperty() //IE9+ 和标准浏览器 支持 查看属性的 ...
- $CF19A\ World\ Football\ Cup$
炒鸡\(6\)批的模拟题. 注意的是输入 把握好空格 大小写. 根据题目的这句话来排序 积分榜是按照以下原则制作的:胜利一个队得3分,平分1分,失败0分. 首先,球队按积分顺序排在积分榜上,分数相等比 ...
- keystore找回密码
昨天准备给自己的应用发布一个新版本,在apk打包时,发现之前的用的keystore密码忘了. 蛋碎了一地,我把我所能想到的密码都试了一遍(注:我平常在各个门户网站注册基本上用的都是那几个字母和数字组合 ...
- .Net MVC 前台验证跟后台验证
前台验证: 首先你得有一个参数类,参数类代码如下 验证标记总结 [DisplayName("邮箱:")] [Required(ErrorMessage = " ...
- LN : JSON (利用C++实现JSON)
Appreciation to our TA, 王毅峰, who designed this task. 问题描述 JSON, JavaScript Object Notation,is an fle ...