python-生成器、迭代器、装饰器
动态语言和静态语言
动态语言可以在运行的过程中修改代码,例如python在运行的过程中给已创建好的类添加属性和方法。
静态语言在编译时已经确定好代码,在运行过程中不能修改代码
那么问题来了,如果我们不想在python的运行中,或者让别人在调用我们的模块时添加新的属性,我们就需要使用python定义的一个特殊变量:_slots_
_slots_
在定义一个类的时候使用这个变量,可以限制类的实例添加属性
class Person(object):
__slots__ = ('name','age')
p = person()
p.name = 'laowang'
p.age = 20
p.heigt = 180
>> AttributeError: Person instance has no attribute 'score'
注意:
使用__slots__定义的属性仅针对当前类的实例起作用,对继承自该类的子类不起作用
生成器
首先思考一个问题,当某个函数隔一段时间需要去从一个列表中获取一个数据,那假设这个函数要不断获取1百万个数据时,需要先把这一百万个数据全都放在列表里吗?答案是否定的,这会非常浪费内存空间。所以生成器产生了,在python中,一边循环一边计算的机制成为生成器:generator,我是这样理解的:先定义一个按需求循环生产数据的对象,不需要一次全部生产出来,当每次另一边需要时,从这个对象里面取出一个值使用就好了,这样可以节省内存空间,同时也方便多个对象来使用它。
创建生成器的方法1
G = ( x*2 for x in range(5))
>>G
>><generator object <genexpr> at 0x7f626c132db0>
>>next(G)
>>0
>>next(G)
>>2
创建生成器的方法2
除了用简单的for方法,我们还可以用函数来实现生成器
def fib(times):
n = 0
a,b = 0,1
while n<times:
yield b
a,b = b,a+b
n+1
retrue 'done'
F = fib(5)
next(F)
>>1
next(F)
>>1
next(F)
>>2
注意,这里使用了yield,相当于执行到yield之后,这个函数会进入阻塞状态。再次调用时,从yield下方开始,再到yield停止。当这个生成器的值被取完后,再次取值程序会报错,返回done。所以一定要注意生成器的剩余值情况。
生成器的send和__next__方法
def gen():
i = 0
while i<5:
temp = yield i
print(temp)
i += 1
>> f = gen()
>> f.__next__()
>> 0
>> f.__next__()
>> None
>> 1
>> f.send('laowang')
>> laowang
>> 2
从上两块代码可见,_next_()方法跟next(f)实现了相同的效果。
前面提到过,yield后,程序会进入阻塞状态,另外赋值语句是先执行等号的右边,再执行等号的左边,所以send方法发送的参数会传递给temp,这就意味着处理数据的函数和生成器可以“沟通”下一步该怎么生产数据
迭代器
迭代是访问集合的一种方式,迭代器是一个可以记住遍历的位置的对象,迭代器对象从集合的第一个元素开始访问,知道所有元素都被访问完结束。
可迭代对象
以直接作用于 for 循环的数据类型有以下几种:
一类是集合数据类型,如 list 、 tuple 、 dict 、 set 、 str 等;
一类是 generator ,包括生成器和带 yield 的generator function。
这些可以直接作用于 for 循环的对象统称为可迭代对象: Iterable。
生成器都是 Iterator 对象,但 list 、 dict 、 str 虽然是可迭代的 Iterable ,却不是迭代器(Iterator)。
总结
- 凡是可作用于 for 循环的对象都是 Iterable 类型;
- 凡是可作用于 next() 函数的对象都是 Iterator 类型
- 集合数据类型如 list 、 dict 、 str 等是 Iterable 但不是 Iterator ,不过可以通过 iter() 函数获得一个 Iterator 对象。
闭包
首先举个例子
def test():
print('233')
x = test
print(id(x))
print(id(test))
x()
>> 140212571149040
>> 140212571149040
>> 233
x实际上是引用了test函数的地址,并不是直接创建了一个新的函数或者使用了一个新的地址。
什么是闭包
在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数及用到的一些变量称之为闭包
def test(num_out):
def test_in(num_in):
print(num_in)
return num_out + num_in
return test_in # 注意返回的是内部函数地址
ret = test(20) # 20给num_out,因为都在一个函数内部,所以变量是可以使用的
print(ret(100)) # 100给num_in
>> 100
>> 120
这里需要注意ret = test(100)直接执行到return test_in 将内部函数的地址返回给ret,所以才能打印。
闭包再理解
def line_conf(a, b):
def line(x):
return a*x + b
return line
line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5))
print(line2(5))
在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个变量的取值,这样,我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,闭包也具有提高代码可复用性的作用。
如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。
闭包可以帮我们把相同规律的事情总结一下,初始化部分交给一个函数,后续操作交给另一个函数,让事情变得层次分明,同时也更方便操作。
闭包思考
1.闭包似优化了变量,原来需要类对象完成的工作,闭包也可以完成
2.由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存
装饰器
作为python3的新特性,装饰器可以提高开发效率,也是python面试中经常会问的问题。
首先对函数名要有这样的理解,名字只是代表了对一个空间的引用,它是可以改变的
def foo():
print('ffffooo')
foo = lambda x:x+1
foo()
>>TypeError: <lambda>() missing 1 required positional argument: 'x'
肯定会报错,因为foo指向的引用地址改变了。
装饰器(decorator)功能
- 引入日志
- 函数执行时间统计
- 执行函数前预备处理
- 执行函数后清理功能
- 权限校验等场景
- 缓存
引入
假设某公司让一个开发部门开发了一些功能,让四个部门分别使用自己的功能,所以要在四个部门使用前验证一次身份,以保证安全性。
已实现的完整功能:
def f1():
print('f1')
def f2():
print('f2')
调用:
# A部门使用f1
# B部门使用f2
# 因为要验证,所以某开发人员又将函数写成了如下形式,
def check()
验证身份
def f1():
check()
print('f1')
def f2():
check()
print('f2')
# 然后再让A部门调用f1,B部门调用f2
虽然也完成了想要的功能,但违反了开放封闭原则,即已实现的功能代码块不允许被修改,但可以扩展(修改复杂业务逻辑的代码容易出现bug,但可以在参数传入前加工一下或数据处理后再进行修正)
- 开放:对扩展开发
- 封闭:已实现的功能代码块
所以需要结合闭包将代码改成如下形式
def w1(func):
def inner():
print('check')
func()
print("i am inner", id(inner))
return inner
@w1
def f1():
print('i am f1', id(f1))
print('f1')
f1()
>>
i am inner 2814185593032
check
i am f1 2814185593032
f1
此时,我们再不修改f1的情况下完成了验证,那代码的执行逻辑是怎么的呢?
最关键的一点是@w1 > f1 = w1(f1)
w1执行后会将f1的引用传递给func(以供inner调用它),然后打印inner函数的id,再将inner的引用返回给f1
f1():执行f1,则f1实际上的引用指向的是inner,所以会执行整个inner函数,而inner中又包含了f1的引用,这样就可以再f1功能执行前加入一些需要的操作,例如之前提到的验证身份
所以@w1就是所谓的装饰器,@函数名 是python中的一种语法糖
再谈装饰器
# 定义函数:完成包裹数据
def makeBold(fn2):
def wrapped2():
return "<b>" + fn2() + "</b>"
return wrapped2
# 定义函数:完成包裹数据
def makeItalic(fn1):
def wrapped1():
return "<i>" + fn1() + "</i>"
return wrapped1
@makeBold
@makeItalic
def test():
return "hello world"
print(test())
>> <b><i>hello world</i></b>
@makeBold
@makeItalic
这里会先执行makeItalic,再执行makeBold,因为这个语法糖需要对函数操作,没有函数就让下面的先执行。
@makeItalic # test = makeItalic(test) -> makeItalic-wrapped1, fn1 -> test(原始内存空间的引用)
@makeBold # test(引用已改变) = makeBold(test) -> makeBold-wrapped2, fn2 -> makeItalic-wrapped1
装饰器举例
被装饰的函数有不定长参数
from time import ctime, sleep
def timefun(func):
def wrappedfunc(*args, **kwargs):
print("%s called at %s"%(func.__name__, ctime()))
func(*args, **kwargs)
return wrappedfunc
@timefun
def foo(a, b, c):
print(a+b+c)
foo(3, 5, 7)
sleep(2)
foo(1, 4, 6)
被装饰的函数有return的处理
from time import ctime, sleep
def timefun(func):
def wrappedfunc():
print("%s called at %s"%(func.__name__, ctime()))
func()
return wrappedfunc
@timefun
def getInfo():
return 'haha'
print(getInfo())
>> None
# getInfo被装饰后,因为return的'haha'是给的func(),而不是getInfo,因此getInfo是没有返回值的,所以打印的是None。
# 需要将func()修改成 return func(),才返回给getInfo,然后打印出'haha'
总结:为了装饰器更通用,一般可以有return
装饰器带参数,在原有装饰器的基础上,设置变量
from time import ctime, sleep
def timefun_arg(pre="hello"):
print('tag1, ', pre)
def timefun(func):
def wrappedfunc():
print("%s called at %s %s"%(func.__name__, ctime(), pre))
return func()
return wrappedfunc
return timefun
@timefun_arg("itcast")
def foo():
print("I am foo")
print('tag2')
sleep(2)
foo()
>>输出:
tag1, itcast
tag2
foo called at Sun Dec 9 21:43:36 2018 itcast
I am foo
可见,当装饰器带有参数后,它会先去执行装饰器函数,而以前没有带入参数时它会直接装饰下方定义的函数。这样的话我们就可以给装饰器添加一些变量或者常用量
python-生成器、迭代器、装饰器的更多相关文章
- Python-Day4 Python基础进阶之生成器/迭代器/装饰器/Json & pickle 数据序列化
一.生成器 通过列表生成式,我们可以直接创建一个列表.但是,受到内存限制,列表容量肯定是有限的.而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面 ...
- python基础(八)生成器,迭代器,装饰器,递归
生成器 在函数中使用yield关键字就会将一个普通的函数变成一个生成器(generator),普通的函数只能使用return来退出函数,而不执行return之后的代码.而生成器可以使用调用一个next ...
- Python之旅Day5 列表生成式 生成器 迭代器 装饰器
装饰器 器即函数,装饰即修饰,意指为其他函数添加新功能 装饰器定义:本质就是函数,功能是为其他函数添加新功能 装饰器涉及的知识点= 高阶函数+函数嵌套+闭包 在遵循下面两个原则的前提下为被装饰者新功能 ...
- Python之迭代器&装饰器&生成器&正则
1.迭代器 迭代器是访问数据集合的一种方式,它只能从集合的第一个元素开始顺序访问,直到最后一个元素结束.类似于linux里的cat命令,只能挨行读取文本内容,不可以跳到中间或者尾部读取(不会把所有的数 ...
- Python生成器、装饰器
## 生成器 - 生成器是用来创建Python序列的一个对象 - 通常生成器是为迭代器产生数据的 - 例如range()函数就是一个生成器 - 每次迭代生成器时,它都会记录上一次调用的位置,并返回下一 ...
- python生成器、装饰器、正则
包子来了[4],被[mayun]吃了! 包子来了[4],被[mahuateng]吃了! 做了两个包子 包子来了[5],被[mayun]吃了! 包子来了[5],被[mahuateng]吃了! 做了两个包 ...
- python的迭代器、生成器、装饰器
迭代器.生成器.装饰器 在这个实验里我们学习迭代器.生成器.装饰器有关知识. 知识点 迭代器 生成器 生成器表达式 闭包 装饰器 实验步骤 1. 迭代器 Python 迭代器(Iterators)对象 ...
- python is、==区别;with;gil;python中tuple和list的区别;Python 中的迭代器、生成器、装饰器
1. is 比较的是两个实例对象是不是完全相同,它们是不是同一个对象,占用的内存地址是否相同 == 比较的是两个对象的内容是否相等 2. with语句时用于对try except finally 的优 ...
- Python 闭包、迭代器、生成器、装饰器
Python 闭包.迭代器.生成器.装饰器 一.闭包 闭包:闭包就是内层函数对外层函数局部变量的引用. def func(): a = "哈哈" def func2(): prin ...
- Python3基础教程(十六)—— 迭代器、生成器、装饰器
在这个实验里我们学习迭代器.生成器.装饰器有关知识. 这几个概念是 Python 中不容易理解透彻的概念,务必把所有的实验代码都完整的输入并理解清楚其中每一行的意思. 迭代器 Python 迭代器(I ...
随机推荐
- Shell笔记-03
前面已经讲到,变量名只能包含数字.字母和下划线,因为某些包含其他字符的变量有特殊含义,这样的变量被称为特殊变量. 例如,$ 表示当前Shell进程的ID,即pid,看下面的代码: $echo $$ 运 ...
- AFSoundManager
iOS audio playing (both local and streaming) and recording made easy through a complete and block-dr ...
- BLE CC2541 串口BootLoader 即 SBL BootLoader 资料 收集
1.[CC254X_Bootloader]SBL(串口Bootloader)使用说明 2.CC2540协议栈高速串口通信解决(UART的DMA方式) 3.[BLE]CC2541之SBL 4.[BLE] ...
- IOS 创建简单表视图
创建简单表视图 此实例主要实现UITableViewDataSource协议中必需要实现的两个方法tableView:numberOfRowsInSection: 和tableView:cellFor ...
- VS Code 常用插件列表
插件列表 Auto Close Tag 自动闭合HTML标签 Auto Rename Tag 修改HTML标签时,自动修改匹配的标签 Bookmarks 添加行书签 Can I Use HTML5.C ...
- iOS12适配及问题记录,Debug正常使用,Release数据为nil的报错
Debug模式数据一切正常,打包出去的ipa,Release模式下数据为nil的错误,经排查,buiding中的Optimization Level, Release设为None,解决问题. IOS1 ...
- npm audit fix
执行npm install 出现如下提醒 added 253 packages from 162 contributors and audited 1117 packages in 42.157s ...
- MongoDB4.0在windows10下的安装与服务配置
本地安装及网页测试 在官网下载最新的安装文件 下载地址 : https://www.mongodb.com/download-center#community 可以在MongoDB官网选择Commun ...
- kubernetes命令式容器应用编排/部署应用/探查应用详情/部署service对象/扩缩容/修改删除对象
部署Pod应用 创建delpoyment控制器对象 [root@master ~]# kubectl run myapp --image=ikubernetes/myapp:v1 --port=80 ...
- 【树形DP】MZOJ_1063_士兵守卫
本题也是这三天来在下写的几篇树形DP之一,但是不知道为什么洛谷上面老是unknown error,...直接去了UVa,说我编译错误...我在想是不是头文件的原因,于是被逼无奈,交了一道c89的代码. ...