装饰器
不得不说,这是对初学者最难以理解的概念了,虽然我学过面向对象,但还是被搞懵逼了。。前面还好理解,主要是后面“装饰器的装饰器”我理解不了。装饰器工厂,根据传入的参数不同去返回不同的装饰器,我不得不在网上查了很多资料去弄懂它,如果没搞清楚这个概念,我是没法安安心心往下学的~~
这里特别要感谢刘志军的这篇文章,完美的解决了我的疑惑:
https://zhuanlan.zhihu.com/p/27449649
装饰器可以在不修改源代码,和原调用方式的基础上,给原有函数增加功能。当然,可以增加功能,只能在原有函数的前面或者后面增加功能,但是没办法改变原有函数的逻辑。要实现一个装饰器,要先掌握几个基本的概念:
1.函数也是一种“变量” 2.高阶函数 3.嵌套函数 4,闭包。
1.函数也是一种“变量”如何理解?
例如,def sayhi()
print ("hello,everyone!")
如果打印sayhi,就是0x0000000001ECE730,一个内存地址。
如果调用sayhi(),内存地址后面加个括号就调用这个函数。
函数定义,把函数体当成字符串存放在内存中,用函数名指向它,调用之前,并不会管函数体是什么内容
2.高阶函数。把一个函数名当做参数传给另一个函数,或者返回值中包含函数名
def dec(func):
print("in decorator")
func()
函数名作为参数传给函数
3,引用计数。
Python内存回收机制,当没有变量名指向某个常量时,即改常量引用计数为0,Python就回收该常量,函数也是一样,没有函数名指向它,或者程序结束就回收该函数体内容
匿名函数 lambda x : x*3 没有函数名指向它,如果给它赋值,calc = lambda x:x*3 就不会马上回收
4.嵌套函数。在函数里定义一个新函数,并返回新函数
def dec(func):
def wrapper(*args, **kwargs):
# 原函数前面增加功能
res = func(*args, **kwargs)
# 原函数后面增加功能
return res
return wrapper
这就是个装饰器decorator,在被装饰函数前面加上:func = dec(func)
改成方便的语法糖写法:@dec
5 .闭包closure
def closure(str):
def decrator(func):
def wrapper():
....
if (str)
func()
else
....
....
return wrapper
return decrator
@closure(str='1') 相当于 func = closure('1')(func)
@closure(str='2') 相当于 func = closure('2')(func)
把参数给了装饰器,可以生产各种类型的装饰器。。根据传的值str判断
生成器
列表生成式:gen = [i for i in range(10)] 生成一个list 名为gen = [0 , 1, 2, 3, 4, 5, 6, 7, 8, 9]这个列表已经保存在内存中,可以根据下标访问其中任意一个元素。
想一下,如果元素有几百万个,是不是都要全部在内存中放着准备取用呢?而且可能并不会用到第几百万个元素,该如何解决内存问题呢??
这时候生成器generator就产生了:
在上面列表生成式方括号改为圆括号变成一个生成器 gen = (i for i in range(10))
这时候gen = generator object <genexpr> at 0x0000000001DF87D8变成了一个地址。当然也就无法通过下标去访问某个元素了。
要访问某个元素就只能通过gen.next()方法去访问。访问一个生成一个。直到出现你想访问的元素。把上面的列表生产式改写一下,换一种写法:
def gen(i): "i表示长度,而且每次取数据都是从1开始循环,从1取到i,yield返回n并记录该中断点" n = 1 while n <= i: yield n n += 1 return "done-----" genner = gen(10) # 第一种调用生成器的方式,用whil true + next while True: # 循环到第10个以后就不能next了,因为跳出了while循环yield不了,就抛异常StopIteration try: print(genner.__next__()) except StopIteration as e: print(e.value) break # 第二种调用生成器的方式,用for循环,这里看出gen(10)是一个迭代器了 for i in gen(10): print(i)
是不是可以认为,生成器用for循环的方式去访问,就叫做“迭代器”??????
注意:生成器的牛逼之处在于yield关键字,你定义的时候就指明了这个生成器的长度。后面每次next()都是接着上一个netx()的后面。所以可以在next()中间做其他事情,因为不会影响结果。
还可以利用send()给yield关键字传值并激活yield, next()只能激活yield
生成器的并行计算:
''' 简单的消费者生产者模型,又是一个简单的协程。 send(a)的作用是将a的值传给yield,并且激活yield。而next()的作用只是激活yield。 消费者模型中,有Alex和Lee两个消费者,吃包子的过程实际上是顺序执行的,只是计算机的速度太快 如果把他们分开显示,可以看成是同步发生的,这就是一个简单的协程 ''' import time def consummer(name): print("%s,准备开始吃包子了!!" % name) while True: baozi = yield print("包子%s 来了,被%s 吃了!" %(baozi, name)) def producer(name): c1 = consummer("Alex") c2 = consummer("Lee") c1.__next__() c2.__next__() for i in range(6): time.sleep(1) print("%s 做了包子%s分两半。。。。。。。。" %(name, i)) c1.send(i) c2.send(i) producer("bob")
------------------------------执行结果-------------------------------------
Alex,准备开始吃包子了!!
Lee,准备开始吃包子了!!
bob 做了包子0分两半。。。。。。。。
包子0 来了,被Alex 吃了!
包子0 来了,被Lee 吃了!
bob 做了包子1分两半。。。。。。。。
包子1 来了,被Alex 吃了!
包子1 来了,被Lee 吃了!
bob 做了包子2分两半。。。。。。。。
包子2 来了,被Alex 吃了!
包子2 来了,被Lee 吃了!
bob 做了包子3分两半。。。。。。。。
包子3 来了,被Alex 吃了!
包子3 来了,被Lee 吃了!
bob 做了包子4分两半。。。。。。。。
包子4 来了,被Alex 吃了!
包子4 来了,被Lee 吃了!
bob 做了包子5分两半。。。。。。。。
包子5 来了,被Alex 吃了!
包子5 来了,被Lee 吃了!
迭代器
1可迭代对象(Iterable): 可直接作用于for循环的对象统称为Iterable可迭代对象,例如 list tuple dict set str 等,判断方法 isinstance(xxx, Iterable)
2.迭代器(Iterator):可以被next()函数调用并不断返回下一个值的对象,它们表示一个惰性的计算序列,可以看出:生成器一定是迭代器,而迭代器不一定是生成器 判断方法: isinstance(xxx,Iterator)
3.Iterable变为Iterator: iter()方法
from collections import Iterator, Iterable def gen(i): "i表示长度,而且每次取数据都是从1开始循环,从1取到i,yield返回n并记录该中断点" n = 1 while n <= i: yield n n += 1 return "done-----" genner = gen(10) list1 = [5,2,3,4,5,6,9] print(isinstance(genner, Iterator)) # 判断是否为迭代器 print(isinstance(genner,Iterable)) # 判断是否为可迭代对象 lst = iter(list1) # 用iter()方法把list1变成迭代器 print(lst.__next__()) # 打印结果5
为什么list dict str 等不是迭代器???
因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被netx()函数调用并不断返回下一个数据,知道没有数据时抛出StopIteration错误。
可以把数据流看成一个有序序列,但不知道序列的长度,只能不断通过netx()按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要下一个数据时才计算
Iterator甚至可以表示一个无限大的数据流,如全体自然数。而list是永远不可能存储全体自然数的。