一、装饰器
器:工具
装饰:为被装饰对象添加新功能
装饰器:装饰的工具
被装饰对象--->>需要添加功能 的函数
装饰器--->>函数
装饰器的作用:在不修改被装饰对象源代码与调用方式的前提下,为其加上新的功能
装饰器必须要遵循的原则:开放封闭原则
为什么要使用装饰器:可以解决代码冗余问题,提高代码的可扩展性
开放封闭原则
开放:对函数功能的添加是开放的。
封闭:对函数功能修改是封闭的。
总结原则如下:
- 不修改被装饰对象源代码
- 不修改被修饰对象调用方式
目的:在遵循1和2原则的基础上扩展新功能
模拟下载电影函数的运行时间 import time def download_m(): print('开始下载电影....') time.sleep(3) print('电影下载完成....') # # start_time = time.time() # download_m() # end_time = time.time() # print(f'消耗时间:{end_time-start_time}') def time_record(): def inner(): start_time = time.time() #统计开始 download_m() #写死了,只能给download_m函数用 end_time = time.time() print(f'消耗时间:{end_time-start_time}') #结束统计,打印消耗时间 return inner func = time_record() #返回的是inner的内存地址 func() #调用的实质其实是inner函数
改进版 import time def download_m(): print('开始下载电影....') time.sleep(3) print('电影下载完成....') # 添加时间功能 def time_record(func): # func = download_m def inner(): start_time = time.time() #统计开始 # download_m() #写死了,只能给download_m函数用 func() # func() = download_m() end_time = time.time() print(f'消耗时间:{end_time-start_time}') #结束统计,打印消耗时间 return inner #返回的是inner的内存地址 res = time_record(download_m) #被装饰对象的调用方式 res()
问题1:被装饰对象,有返回值 def download_m(): print('开始下载电影....') time.sleep(3) print('电影下载完成....') return "小丑.mp4" def time_record(func): def inner(): start_time = time.time() res = func() # func() = download_m() end_time = time.time() print(f'消耗时间:{end_time-start_time}') return res #res = download_m() return inner download_m = time_record(download_m) # time_record(download_m)=inner#重新赋值给download_m download_m()
问题2:被装饰对象,有参数 def download_m(url): print(f'{url}开始下载电影....') time.sleep(3) print('电影下载完成....') return"小泽.mp4" def time_record(func): # func <-- download_movie # url = 'https://www.baidu.com/' # 在闭包函数中 def inner(url): start_time = time.time() res = func(url) # func(url) ---> download_movie(url) end_time = time.time() print(f'消耗时间: {end_time - start_time}') return res return inner download_m = time_record(download_m) download_m('https://www.baidu.com')
问题3: 假如被装饰对象需要接收多个参数 def download_movie(url1,url2): print(f'{url1,url2}开始下载电影....') time.sleep(3) # 等待3秒 print('电影下载成功...') return '小泽.mp4' #装饰器最终版本 def time_record(func): def inner(*args,**kwargs): #*args,**kwargs接受所有的参数 start_time = time.time() res = func(*args,**kwargs) # 将被装饰对象需要接收的任意参数 原封不动传给func end_time = time.time() print(f'消耗时间: {end_time - start_time}') return res return inner download_movie = time_record(download_movie) download_movie(url1='https://www.baidu.com', url2='https://www.hao123.com')
叠加装饰器
叠加装饰器: 在同一个被装饰对象中,添加多个装饰器,并执行。 @装饰1 @装饰2 @装饰3 def 被装饰对象(): pass 注意: 装饰器在调用被装饰对象时才会执行添加的功能。 - 叠加装饰器: - 装饰的顺序: 由下到上装饰 - 执行的顺序: 由上往下 注意: 无论inner中出现任何判断,最后都要返回“调用后的被装饰对象” func(*args, **kwargs)
装饰器可以理解在外面套了一层函数,内部定义函数,外层函数返回内层函数的名称
形式:@+函数名a即可 相当于func=a(func)=inner,func是被装饰函数,理解为修改了函数名。类装饰器表示修改了类名
def w2(f):
def inner():
print('w2')
res=f()
print('w2end')
return res
return inner
def wrappers(f):
#print('h')
def inner():
print('1')
res=f()
print('2')
return res
return inner
@w2
@wrappers
def aaa():
print('aaa')
if __name__=='__main__':
aaa()
输出结果:
w2
1
aaa
2
w2end
无参装饰器: 装饰在被装饰对象时,没有传参数的装饰器。
有参装饰器: 本质上就是在无参装饰器上套了一个外层函数,无参装饰器可以引用外层函数的名字。
装饰器模板
def wrapper(func): def inner(*args, **kwargs): 为被装饰对象添加新功能 res = func(*args, **kwargs) # 调用被装饰对象,得到返回值 为被装饰对象添加新功能 return res return inner def func1(): pass func1 = wrapper(func1) func1() # inner()
装饰器的语法糖
装饰器语法糖,是属于装饰器的。
@:装饰器的的语法糖
注意: 在使用装饰器语法糖时,装饰器必须定义在被装饰对象之上。
import time # 统计函数执行时间装饰器 def wrapper(func): # 被装饰对象 def inner(*args, **kwargs): # 被装饰对象的参数 start_time = time.time() # 调用前增加新功能 res = func(*args, **kwargs) # 调用被装饰对象,并接收返回值 end_time = time.time() # 调用后添加新功能 print(end_time - start_time) return res return inner @wrapper # download= wrapper(download_m) 下面相当于没有了 def download_m() print('开始下载电影....') time.sleep(3) print('电影下载完成....') download_m()
------------ 这不是装饰器叠加--------------------------
user,passwd='alex','123'
#def login_flag(flag=False):
flag=False #login_state=Flase
home_login_flag="jingdong" #home的登录状态 应该是从其他地方传过来的参数
def login_type(auth_type):
def login(f):
def inner():
global flag
f()
if home_login_flag==auth_type:
if flag==False: #if flag:
username=input("username")
password=(input('passwd:'))
if user==username and passwd==password:
flag=True
print("wlecom")
else:
pass
else:
print('denglucuowu')
return inner
return login
@login_type('jingdong') #authentication 认证 auth_type 验证种类 先执行login_type()函数
def home():
print("welcom to home page")
@login_type("weixing")
def finance():
print("welcom to finance page")
@login_type("")
def book():
print("welcom to book page")
# home()
# finance()
# book()
定义:
可迭代对象(Iterable)
Python中任意的对象, 只要它定义了可以返回⼀个迭代器的__iter__⽅法, 或者定义了可以⽀持下标索引的__getitem__⽅法(这些双下划线⽅法会在其他章节中全⾯解释),那么它就是⼀个可迭代对象。 简单说, 可迭代对象就是能提供迭代器的任意对象
迭代器(Iterator)
任意对象, 只要定义了next(Python2) 或者__next__⽅法, 它就是⼀个迭代器。
⽣成器(Generators)
⽣成器也是⼀种迭代器, 但是你只能对其迭代⼀次。 这是因为它们并没有把所有的值存在
内存中, ⽽是在运⾏时⽣成值。 你通过遍历来使⽤它们, 要么⽤⼀个“for”循环, 要么将它
们传递给任意可以进⾏迭代的函数和结构。 ⼤多数时候⽣成器是以函数来实现的。 然⽽,
它们并不返回⼀个值, ⽽是yield(暂且译作“⽣出”)⼀个值。
二、迭代器
定义:迭代取值的工具, 它可以迭代取值。
迭代:是指重复迭代,每一次迭代的结果都是基于上一次的结果而来的
-可迭代对象:所有序列类型:list、tuple、dict、str、set、f(文件)
#依赖于索引取值 goods=['mac','lenovo','acer','dell','sony'] index=0 while index < len(goods): print(goods[index]) index+=1
dict1 = {'name':'bob','age':18,'sex':'male'} iter_dict1 = dict1.__iter__() #iter_dict1是一个迭代器对象 print(iter_dict1.__next__()) print(iter_dict1.__next__()) print(iter_dict1.__next__()) >>>name >>>age >>>male
补充: list1 = ['tank', 'jason鸡哥', 'sean', '饼哥'] iter_list1 = list1.__iter__() while True: # 补充: try:获取异常 try: print(iter_list1.__next__()) #报错 #立即触发此代码 StopIteration except StopIteration: break
凡是内部有.__iter__()方法的都是可迭代对象。 例如: str='hello' str1.__iter__() -获取迭代器: 通过可迭代对象.__iter__(),得到的返回值就是"迭代器对象 -如何迭代取值: 迭代器对象.__next__(),每次执行,都会从迭代器对象中取出一个值 -迭代器对象的优点: 1. 不依赖于索引迭代取值 2. 节省内存空间。 缺点: 1.取指定某个值麻烦 2.每次取值都要从第一个值开始,无法同过索引取值 3.无法通过len()计算长度 - 迭代器本质上是一个可迭代对象 - 文件本质上既是迭代器对象,也是可迭代对象。 - 可迭代对象不一定是迭代器对象 for循环原理: for i in 可迭代对象: - in: 会将可迭代对象自动调用.__iter__()变成迭代器对象 - for循环内置捕获异常机制
set1 = '1, 2, 3, 4' iter_set1 = set1.__iter__() #iter_set1 迭代器 print(iter_set1.__iter__() is iter_set1) #True list1 = [1, 2, 3, 4] iter_list1 = list1.__iter__() print(iter_list1 is list1) #False
三、生成器
但凡在函数内部包含关键字yield,调用函数时,函数体代码不会执行,但会返回一个结果,该结果就是一个生成器
-yield: 只能在函数内部定义 每次yield都会往生成器对象中添加一个值, yield可以保存函数的暂停状态 yield与return: 相同点: 返回值的个数都是无限制的。 不同点: return只能返回一次值,yield可以返回多次值
# 自定义的迭代器: def func(): print('form func') yield 1 res = func() #res是一个生成器 print(res) #<generator object func at 0x0000016D5A0DEF48> #当我们通过.__next__取值时,才会执行函数体代码。 def func(): print('from func') yield 1 res = func() print(res.__next__()) def func(): print('开始准备下蛋') print('一个鸡蛋') yield '鸡蛋1' print('第二个鸡蛋') yield '鸡蛋2' print('第三个鸡蛋') yield '鸡蛋3' print('取最后一个鸡蛋,查看是否还有') res = func() #res是迭代器对象 # print(next(res)) # print(next(res)) # print(next(res)) # print(next(res)) #StopIteration 报错 迭代器对象.__next__() ==next(迭代器对象) print(res.__next__()) #当我们通过.__next__取值时,才会执行函数体代码 print(res.__next__()) print(res.__next__()) print(res.__next__()) #StopIteration 报错 # 循环10次 for i in range(1,11): print(i) # python2:range(1,5)--->[1,2,3,4] # python3:range(1,5) --->range对象 --->生成器 --->迭代器 #自定义range功能,创建一个自定义的生成器 def my_range(start,end,move): while start<end: yield start start +=move g_range = my_range(1,5,2) #g_range 是生成器 print(g_range)