⽣成器
1. 什么是⽣成器
通过列表⽣成式,我们可以直接创建⼀个列表。但是,受到内存限制,列表 容量肯定是有限的。⽽且,创建⼀个包含100万个元素的列表,不仅占⽤很 ⼤的存储空间,如果我们仅仅需要访问前⾯⼏个元素,那后⾯绝⼤多数元素 占⽤的空间都⽩⽩浪费了。所以,如果列表元素可以按照某种算法推算出 来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必 创建完整的list,从⽽节省⼤量的空间。在Python中,这种⼀边循环⼀边计算 的机制,称为⽣成器:generator。
2. 创建⽣成器⽅法1
要创建⼀个⽣成器,有很多种⽅法。第⼀种⽅法很简单,只要把⼀个列表⽣ 成式的 [ ] 改成 ( )
输入:L=[ x*2 for x in range(5)] 输出:print(L) 结果:[0, 2, 4, 6, 8] 输入:G=(x *2 for x in range(5)) 输出:print (G) 结果:<generator object <genexpr> at 0x03490EA0>
创建 L 和 G 的区别仅在于最外层的 [ ] 和 ( ) , L 是⼀个列表,⽽ G 是⼀个 ⽣成器。我们可以直接打印出L的每⼀个元素,但我们怎么打印出G的每⼀个 元素呢?如果要⼀个⼀个打印出来,可以通过 next() 函数获得⽣成器的下⼀ 个返回值:
G=(x *2 for x in range(5)) print(next(G)) print(next(G)) print(next(G)) 0 2 4
G=(x *2 for x in range(5)) for i in G: print(i) 0 2 4 6 8
⽣成器保存的是算法,每次调⽤ next(G) ,就计算出 G 的下⼀个元素的值, 直到计算到最后⼀个元素,没有更多的元素时,抛出 StopIteration 的异常。 当然,这种不断调⽤ next() 实在是太变态了,正确的⽅法是使⽤ for 循环, 因为⽣成器也是可迭代对象。所以,我们创建了⼀个⽣成器后,基本上永远 不会调⽤ next() ,⽽是通过 for 循环来迭代它,并且不需要关⼼ StopIteration 异常。
总结
⽣成器是这样⼀个函数,它记住上⼀次返回时在函数体中的位置。对⽣成器 函数的第⼆次(或第 n 次)调⽤跳转⾄该函数中间,⽽上次调⽤的所有局部 变量都保持不变。
⽣成器不仅“记住”了它数据状态;⽣成器还“记住”了它在流控制构造(在命令 式编程中,这种构造不只是数据值)中的位置。
⽣成器的特点:
1. 节约内存
2. 迭代到下⼀次的调⽤时,所使⽤的参数都是第⼀次所保留下的,即是 说,在整个所有函数调⽤的参数都是第⼀次所调⽤时保留的,⽽不是新 创建的
迭代器
迭代是访问集合元素的⼀种⽅式。迭代器是⼀个可以记住遍历的位置的对 象。迭代器对象从集合的第⼀个元素开始访问,直到所有的元素被访问完结 束。迭代器只能往前不会后退。
1. 可迭代对象
以直接作⽤于 for 循环的数据类型有以下⼏种:
⼀类是集合数据类型,如 list 、 tuple 、 dict 、 set 、 str 等;
⼀类是 generator ,包括⽣成器和带 yield 的generator function。
这些可以直接作⽤于 for 循环的对象统称为可迭代对象: Iterable 。
2. 判断是否可以迭代
可以使⽤ isinstance() 判断⼀个对象是否是 Iterable 对象:
总结
凡是可作⽤于 for 循环的对象都是 Iterable 类型;
凡是可作⽤于 next() 函数的对象都是 Iterator 类型
集合数据类型如 list 、 dict 、 str 等是 Iterable 但不是 Iterator ,不过可 以通过 iter() 函数获得⼀个 Iterator 对象。
装饰器
装饰器是程序开发中经常会⽤到的⼀个功能,⽤好了装饰器,开发效率如⻁ 添翼,所以这也是Python⾯试中必问的问题,但对于好多初次接触这个知识 的⼈来讲,这个功能有点绕,⾃学时直接绕过去了,然后⾯试问到了就挂 了,因为装饰器是程序开发的基础知识,这个都不会,别跟⼈家说你会 Python, 看了下⾯的⽂章,保证你学会装饰器。
初创公司有N个业务部⻔,1个基础平台部⻔,基础平台负责提供底层的功 能,如:数据库操作、redis调⽤、监控API等功能。业务部⻔使⽤基础功能 时,只需调⽤基础平台提供的功能即可。如下:
def f1(): print('f1') def f2(): print('f2') def f3(): print('f3') def f4(): print('f4')
############### 业务部⻔A 调⽤基础平台提供的功能 ############### f1() f2() f3() f4() ############### 业务部⻔B 调⽤基础平台提供的功能 ###############
f1() f2() f3() f4()
⽬前公司有条不紊的进⾏着,但是,以前基础平台的开发⼈员在写代码时候 没有关注验证相关的问题,
即:基础平台的提供的功能可以被任何⼈使⽤。 现在需要对基础平台的所有功能进⾏重构,为平台提供的所有功能添加验证 机制,即:执⾏功能前,先进⾏验证。
⽼⼤把⼯作交给 Low B,他是这么做的:
跟每个业务部⻔交涉,每个业务部⻔⾃⼰写代码,调⽤基础平台的功能 之前先验证。诶,这样⼀来基础平台就不需要做任何修改了。太棒了, 有充⾜的时间泡妹⼦...
当天Low B 被开除了…
⽼⼤把⼯作交给 Low BB,他是这么做的:
############### 基础平台提供的功能如下 ###############
def f1(): # 验证1 # 验证2 # 验证3 print ('f1') def f2(): # 验证1 # 验证2 # 验证3 print ('f2') def f3(): # 验证1 # 验证2 # 验证3 print ('f3') def f4(): # 验证1 # 验证2 # 验证3 print ('f4')
############### 业务部⻔不变 ############### ### 业务部⻔A 调⽤基础平台提供的功能###
过了⼀周 Low BB 被开除了…
⽼⼤把⼯作交给 Low BBB,他是这么做的:
只对基础平台的代码进⾏重构,其他业务部⻔⽆需做任何修改 ############### 基础平台提供的功能如下 ############### def check_login():
# 验证1
# 验证2
# 验证3
pass def f1(): check_login() print('f1') def f2(): check_login() print('f2') def f3(): check_login() print('f3') def f4(): check_login() print('f4')
⽼⼤看了下Low BBB 的实现,嘴⻆漏出了⼀丝的欣慰的笑,语重⼼⻓的跟 Low BBB聊了个天:
⽼⼤说:
写代码要遵循 开放封闭 原则,虽然在这个原则是⽤的⾯向对象开发,但是也 适⽤于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改, 但可以被扩展,即:
封闭:已实现的功能代码块
开放:对扩展开发
如果将开放封闭原则应⽤在上述需求中,那么就不允许在函数 f1 、f2、f3、 f4的内部进⾏修改代码,⽼板就给了Low BBB⼀个实现⽅案:
def w1(func): def inner(): # 验证1 # 验证2 # 验证3 func() return inner @w1 def f1(): print ('f1') @w1 def f2(): print ('f2') @w1 def f3(): print ('f3') @w1 def f4(): print ('f4')
对于上述代码,也是仅仅对基础平台的代码进⾏修改,就可以实现在其他⼈ 调⽤函数 f1 f2 f3 f4 之前都进⾏【验证】操作,并且其他业务部⻔⽆需做任 何操作。
Low BBB⼼惊胆战的问了下,这段代码的内部执⾏原理是什么呢?
⽼⼤正要⽣⽓,突然Low BBB的⼿机掉到地上,恰巧屏保就是Low BBB的⼥ 友照⽚,⽼⼤⼀看⼀紧⼀抖,喜笑颜开,决定和Low BBB交个好朋友。
详细的开始讲解了:
单独以f1为例:
def w1(func): def inner(): #验证1 #验证2 #验证3 func() return inner @w1 def f1(): print('f1')
python解释器就会从上到下解释代码,步骤如下:
1. def w1(func): ==>将w1函数加载到内存
2. @w1
没错,从表⾯上看解释器仅仅会解释这两句代码,因为函数在 没有被调⽤之 前其内部代码不会被执⾏。
从表⾯上看解释器着实会执⾏这两句,但是 @w1这⼀句代码⾥却有⼤⽂章,@函数名是python的⼀种语法糖。
上例@w1内部会执⾏⼀下操作: 执⾏w1函数
执⾏w1函数,并将@w1下⾯的函数作为w1函数的参数,即:@w1 等价于w1(f1)所以,内部就会去执⾏: def inner(): #验证 1 # #验证 2 # #验证 3 f1() #func是参数,此时func等于f1 return inner #返回的inner,inner代表的是函数,⾮执⾏函数,其实就是将原来的f1函数塞进另外⼀个函数中 w1的返回值 将执⾏完的w1函数返回值赋值给@w1下⾯的函数的函数名f1即将w1 的返回值再重新赋值给f1,即: 新f1 = def inner():
# 验证 1 # #验证 2 # #验证 3 原来f1() return inner 所以,以后业务部⻔想要执⾏f1函数时,就会执⾏新f1函数,在新f1 函数内部先执⾏验证,再执⾏原来的f1函数,然后将原来f1 函数的返回 值返回给了业务调⽤者。 如此⼀来,即执⾏了验证的功能,⼜执⾏了原来f1函数的内容,并将原f1函 数返回值返回给业务调⽤着 LowBBB 你明⽩了吗?要是没明⽩的话,我晚上去你家帮你解决吧!!!
3. 再议装饰器
#定义函数:完成包裹数据 def makeBold(fn): def wrapped(): return "<b>" + fn() + "</b>" return wrapped #定义函数:完成包裹数据 def makeItalic(fn): def wrapped(): return "<i>" + fn() + "</i>" return wrapped @makeBold def test1(): return "hello world-1" @makeItalic def test2(): return "hello world-2" @makeBold @makeItalic def test3(): return "hello world-3" print(test1())) print(test2())) print(test3())) 运⾏结果: <b>hello world-1</b> <i>hello world-2</i> <b><i>hello world-3</i></b>
4. 装饰器(decorator)功能
1. 引⼊⽇志
2. 函数执⾏时间统计
3. 执⾏函数前预备处理
4. 执⾏函数后清理功能
5. 权限校验等场景
6. 缓存
5. 装饰器示例
例1:⽆参数的函数
from time import ctime, sleep def timefun(func): def wrappedfunc(): print ("%s called at %s" % (func.__name__, ctime ())) func () return wrappedfunc @timefun def foo(): print ("I am foo") foo () sleep (2) foo ()
上面代码理解装饰器执行行为可理解成
foo = timefun(foo)
#foo先作为参数赋值给func后,foo接收指向timefun返回的wrappedfuncfoo()
#调⽤foo(),即等价调⽤wrappedfunc() #内部函数wrappedfunc被引⽤,所以外部函数的func变量(⾃由变量)并没有释放
#func⾥保存的是原foo函数对象
浙公网安备 33010602011771号