装饰器
装饰器:本质是函数
功能:为其他函数添加附加功能。(为房子装饰)
原则:
- 不能修改被装饰函数的源代码
- 被装饰函数的调用方式不能被修改
实现装饰器的知识储备:
1.函数即"变量",函数名即为'门牌号'(类似变量名),函数体即为变量内容。del可删除门牌号,若没有门牌号指向函数体函数体即被清空。
2.高阶函数:满足下列条件之一:
a.把一个函数名当作实参传给另外一个函数(在不修改被装饰函数的源代码情况下为其添加新功能。
b.返回值中包含函数名(不修改函数的调用方式)
3.嵌套函数
我们希望实现的功能
为函数插入一个功能:检测出一个函数的的运行时间而且不改变其调用方式
#正常的思维
import time def bar(): time.sleep(3) print('in the bar') def test1(func): start_time = time.time() func() stop_time = time.time() print('the running time is %s'%(stop_time-start_time)) test1(bar)#此时调用方式改变了,不符合装饰器原则
装饰器运行的原理
import time def bar(): time.sleep(3) print('in the bar') def test2(func): print(func) return func bar = test2(bar)#运行bar()的同时也会执行test2函数,@如果在bar前面加上@test2 调用bar()的时候会自动执行这条语句 bar()
高阶函数+嵌套函数=》装饰器
整个模块的执行顺序: 执行:1.为timmer,test1函数名分配空间 2.执行@timmer语句->test1 = timmer(test1) 2.1 timmer的func参数变成test1的内存地址 2.2 为wrapper函数分配空间 2.3 test1的内存地址指向wrapper 3.执行test1() 3.1 按顺序执行wrapper函数 3.2 执行到func()语句时候返回test1函数按顺序执行test1函数 3.3 执行完test1 返回wrapper函数中继续往下执行直到执行完wrapper函数 程序结束
import time def timmer(func): def warpper(*args,**kwargs): start_time = time.time() func() stop_time=time.time() print('the func run time is %s'%(stop_time-start_time)) return warpper @timmer def test1(): time.sleep(3)#代表函数运行逻辑所花费的时间 print('in the test1') test1() ----
@timmer的等价代码
test1 = timmer(test1)
----
#难点:在3.2时 执行内部的wrapper语句时由于func这个value是在之前传入的,是否还有效? 小实验: def _123(x): def _456(): print(x) return _456 x = _123(1) x()#打印出1 #答:有效
装饰器的实例运用->用户登录系统
装饰器应用实例->登陆系统 user,passwd = 'negu','negu0' def auth(auth_type): print('auth func:',auth_type) def outer_wrapper(func): def wrapper(*args,**kwargs): if auth_type == 'local': username = input('user: ').strip() password = input('password: ').strip() if user == username and passwd == password: print(' 33[32;1m User has passed authentication 33[0m') func(*args,**kwargs) else: exit(' 33[31;1minvalid username or password 33[0m') #return 'from home' elif auth_type == 'ldap': print('ldap') return wrapper return outer_wrapper def index(): print('welcome to index page.') @auth(auth_type = 'local')#home = wrapper(),加上括号以后会提前执行wrapper def home(): print('welcome to home page.') return 'from home'#用装饰器以后没有输出任何值,要在装饰器内部给func赋值才有。 @auth(auth_type = 'ldap') def bbs(): print('welcome to bbs page.') index() print(home()) bbs()
迭代
列表生成式、生成器
p = [i*2 for i in range(10) ] print(p) 等价于 for i in range(10): print i*2
通过列表生成式,可以直接创建一个列表。但内存受限制,列表容量有限。,而且创建一个包含100W个元素的列表,不仅占用很大空间,如果仅仅访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以如果列表可以按照某种算法推算出来那我们可以在循环过程中不断推算算出后面的元素,这样不必创建完整的列表,从而节省大量的空间。在py中,这种 一边循环一边计算的机制称为生成器。(generator)
g = (i*2 for i in range(10))#用生成器生成100W个元素一般感觉不到任何延迟而生成式会有延迟,因为执行命令的时候根本没有生成,除非调用才生成。 print(1,g.__next__()) #输出0,一般不能调用g[5]之类的因为还没生成,一般只能用循环去取值。 print(2,g.__next__()) #输出2 print(3,g.__next__()) #输出4 for i in g:#它的调用方式多次的:g.__next__(唯一方法),生成器省内存的方法:只保留当前循环的位置的数据,不知前后数据。只能用for循环调用。 print(i)
生成器应用实例
import time def consumer(name): print("%s 准备吃包子啦!" %name) while True: baozi = yield#第一句为negu准备吃包子啦!,可见yield为一个断点。 print("包子[%s]来了,被[%s]吃了!" %(baozi,name)) # c = consumer('negu')#如果是consumer是函数这样赋值直接执行函数,而genrator不会执行。 # c.__next__() # b1 = '韭菜馅' # c.send(b1)#出现这个send将会默认执行一次c.__next__,并给yield赋值。 # #c.__next__() def producer(name): c = consumer('A') c2 = consumer('B') c.__next__()#相当于打印print('xx开始准备吃包子啦') c2.__next__() print("老子开始准备做包子啦!") for i in range(2): time.sleep(1) print("%s做了2个包子!"%name) c.send(i) c2.send(i) producer("negu")#这个程序同时执行三个任务,顾客1,顾客2吃包子,negu做包子。
输出: A 准备吃包子啦! B 准备吃包子啦! 老子开始准备做包子啦! negu做了2个包子! 包子[0]来了,被[A]吃了! 包子[0]来了,被[B]吃了! negu做了2个包子! 包子[1]来了,被[A]吃了! 包子[1]来了,被[B]吃了!
可迭代对象、迭代器
可直接用于for循环的数据类型:str,tuple,list,dict,set,文件句柄。
可直接用于for循环还有一个就是生成器generator和带yield的function。
可用于for循环的变量对象称为可迭代对象(iterable)
可以用collection中的iterable类、以及内置isinstance来判断是否是可迭代对象。 from collections import Iterable print('元组',isinstance((1,2,3),Iterable))#true print('列表',isinstance([1,2,3],Iterable))#true print('字典',isinstance({1,2,3},Iterable))#true print('字符串',isinstance('1,2,3',Iterable))#true print('集合',isinstance(set([1,2,3]),Iterable))#true #通过内置函数iter()可以把dict、list、tuple变成迭代器对象。
为什么list、tuple、dict不是个iterator????:因为python的iterator表示的是一个数据流,iterator对象可以被next()函数调用。
可以把这个数据流看成一个有序的序列,但我们不能提前知道序列长度,只能通过不断的next()计算下一个数据,所以iterator是惰性的。只有需要返回下一个数据的时候才会计算而元组、列表等变量是储存在内存中可以随时提取的。
在python3中range()函数就是一个迭代器
for i in range (10): [代码命令] 等价于 it = iter([1,2,3,4,5,6,7,8,9,10]) while True : try : i = it.__next__ [代码命令] except StopIteration: break