装饰器
1、什么是装饰器?
不修改函数的调用方式,还能再原来函数的基础上增加功能。
2、装饰器原则:开放封闭原则
开放:对扩展时开放的
封闭:对修改是封闭的
3、装饰器的通用写法:
def wrapper(func): # 装饰器 def inner(*args, **kwargs): # 利用*args和**kwargs接受任意参数:位置参数和关键字参数 ret = func(*args, **kwargs) # 接收函数的返回值 return ret return inner @wrapper # 装饰器的重点:语法糖:这里实际上是wahaha = wrapper(wahaha),不过这样写应该写到定义的函数后 def wahaha(a,b): # 原函数 sum = a + b return sum ret = wahaha(3, 4) # 函数调用且接受返回值 print(ret)
ps:小知识补充一:time模块
import time # 导入time模块 def wrapper(func): def inner(*args, **kwargs): start = time.time() # time()可以查看当前时间(从1970到现在) ret = func(*args, **kwargs) end = time.time() print(end - start) # 可以查看代码执行时间 return ret return inner @wrapper def wahaha(a,b): sum = a + b time.sleep(0.1) # 让代码停止0.1秒 return sum # wahaha = wrapper(wahaha) ret = wahaha(3, 4) print(ret)
4、装饰器进阶
完美装饰器
import time from functools import wraps def wrapper(func): @wraps(func) # 完美装饰函数 def inner(*args, **kwargs): start = time.time() ret = func(*args, **kwargs) end = time.time() print(end - start) return ret return inner @wrapper def wahaha(a,b): sum = a + b time.sleep(0.1) return sum print(wahaha.__name__) # 查看函数名
5、带参数的装饰器
import time from functools import wraps flag = 1 # 通过设置标志位可与以使用户更方便的决定用或不用装饰器 def Flag(flag): # 在原本装饰器的外层再套一层装饰器,进行一次参数的传递 def wrapper(func): @wraps(func) def inner(*args, **kwargs): if flag: start = time.time() ret = func(*args, **kwargs) end = time.time() print(end - start) return ret else: ret = func(*args, **kwargs) return ret return inner return wrapper @Flag(flag) def wahaha(a,b): sum = a + b time.sleep(0.1) return sum ret = wahaha(1,2) print(ret)
6、多个装饰器装饰同一个函数
def wrapper1(func): # 1 def inner1(*args, **kwargs): # 4 print('before func inner1') # 14 ret1 = func(*args, **kwargs) # 15 wahaha() # 18 ret1 = 123 print('after func inner1') # 19 return ret1 # 20 return inner1 # 5 def wrapper2(func): # 2 def inner2(*args, **kwargs): # 8 print('before func inner2') # 12 ret2 = func(*args, **kwargs) # 13 inner1() # 21 ret2 = ret1 = 123 print('after func inner2') # 22 return ret2 #23 return inner2 # 9 @wrapper2 # 7 wrapper2(inner1) # 10 wahaha = inner2 @wrapper1 # 3 wrapper1(wahaha) # 6 wahaha = inner1 def wahaha(): print('wahaha is good') # 16 return 123 # 17 ret = wahaha() # 11 inner2() #24 ret = ret2 = 123 print(ret) # 25
迭代器
1、双下方法:__iter__()加了双下划线的方法
2、可迭代协议:只要含有__iter__方法的都是可迭代的 判断方法:'__iter__'in dir(方法名)
可以被for循环的都是可迭代的
3、迭代器协议:内部含有__iter__和__next__的方法就是迭代器 判断方法: '__iter__' in dir() and '__next__'in dir()
可迭代的.__iter__()就得到一个迭代器
迭代器中的__next__()方法可以一个一个的获取值
for循环其实就是在使用迭代器
4、迭代器的优点:
(1)使用方便,一个迭代器只能从头到尾取值,且只能取一次
(2)节省内存空间(可迭代对象是一个个给你而不是一次性读取,不会占用大块内存,每次只给一个)
l = [1, 2, 3, 4, 5] g = l.__iter__() print(g.__next__()) # 每次返回列表中的一个值 print(g.__next__()) # 每次返回列表中的一个值 print(g.__next__()) # 每次返回列表中的一个值 print(g.__next__()) # 每次返回列表中的一个值
生成器
1、本质上是一种迭代器,生成器与迭代器都是惰性运算
2、生成器函数:本质上就是我们自己写的函数,只要含有yield的函数都是生成器函数,且yield不能与return共用,且只能在函数内使用
生成器函数执行一次后获得一个生成器,此时函数并没有执行
3、生成器:
yield在返回值时和return有着相同的作用
def generater(): print(1) yield 'a' print(2) yield 'b' g = generater() # 此时只是获得一个生成器 ret = g.__next__() # g.__next__()这时函数才真正执行,执行到第一个yield处 print(ret) ret = g.__next__() # g.__next__()函数接着上次停止的地方执行,执行到下一个yield处 print(ret)
因为生成器的本质是迭代器,所以一个生成器只能从头到尾取一次值,当取完值时在执行生成器.__next__()会报错
4、从生成器中取值的几个方法:
next、for循环、数据类型的强制转换(比较占用内存)
5、生成器函数进阶一:send函数
def generator(): content = yield 1 print('===',content) content = yield 2 print('***',content) content = yield 3 g = generator() ret = g.__next__() # 在第一次使用生成器时,需用next获取到一个值 print(ret) ret = g.send(10) # send函数与next函数都有获取下一个值的效果,但send函数可以在上一个yield的地方传一个参数 print(ret) ret = g.send(20) print(ret) ret = g.send(30) # 最后一个yield不能接收外部的值 print(ret)
6、生成器函数进阶二:获取移动平均值
def average(): sum = 0 count = 0 avg = 0 while True: num = yield avg # 生成器激活到这里就停止 sum += num count += 1 avg = sum/count a = average() a.__next__() # 激活生成器 a1 = a.send(10) # 在停止位置给num传一个值 a2 = a.send(20) print('%s,%s'%(a1,a2))
ps:小技巧
def generator(): a = 'asdfg' b = '12345' yield from a # 可迭代类型数据都可以 yield from b g = generator() for i in g: print(i)
7、生成器表达式
1)列表推导式
list=['%s'%i for i in [1,2,3,4,5] if i>3] print(list)
2)生成器表达式
g = (i for i in range(10)) for i in g: print(i)
生成器表达式与列表推导式的括号不同,返回的值不同。生成器表达式返回的是一个生成器,几乎不占用内存。
当碰到列表推导式相关面试题时,可以展开进行一步步的推导。
3)字典推导式
# 将字典的键值互换 dic = {1:'a',2:'b'} dic1 = {dic[i]:i for i in dic} print(dic1)
4)集合推导式
set1 = {i**i for i in [1,-1,2]} print(set1) # 集合自带去重功能