Python之三大器
一、装饰器
闭包函数
装饰器简介
装饰器模板
装饰器的语法糖
二、迭代器
三、生成器
一、装饰器
闭包函数
闭:指的是定义在函数内部的函数
比如手机是闭包函数(内层函数),被手机包装盒 (外层函数) 包裹起来,
手机可以使用包装盒中的东西,内层函数可以引用外层函数的名字。
闭包函数:定义在函数内部的函数,并且该函数包含对外部函数作用域中名字的引用,该函数就称为闭包函数。
闭包函数是 函数嵌套、函数对象、名称空间与作用域 结合体。
基本形式:
def outer():
name ='egon'
def inner():
print('my name is %s' %name)
return inner
# print(outer())
inner=outer() #实现了外部调用内部的函数
inner()
注意:作用域关系在函数定义阶段就定死了,与调用位置无关
闭包函数外面最多套两层函数就够了
装饰器简介
器:工具
装饰:为被装饰对象添加新功能
装饰器:装饰的工具
被装饰对象--->>需要添加功能 的函数
装饰器--->>函数
装饰器的作用:在不修改被装饰对象源代码与调用方式的前提下,为其加上新的功能
装饰器必须要遵循的原则:开放封闭原则
为什么要使用装饰器:可以解决代码冗余问题,提高代码的可扩展性
开放封闭原则
开放:对函数功能的添加是开放的。
封闭:对函数功能修改是封闭的。
总结原则如下:
- 不修改被装饰对象源代码
- 不修改被修饰对象调用方式
目的:在遵循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)
装饰器模板
无参装饰器: 装饰在被装饰对象时,没有传参数的装饰器。
def wrapper(func):
def inner(*args, **kwargs):
为被装饰对象添加新功能
res = func(*args, **kwargs) # 调用被装饰对象,得到返回值
为被装饰对象添加新功能
return res
return inner
def func1():
pass
func1 = wrapper(func1)
func1() # inner()
有参装饰器: 本质上就是在无参装饰器上套了一个外层函数,无参装饰器可以引用外层函数的名字。
def 有参装饰器(x, y, z)
def outter(func):
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
return res
return wrapper
return outter
@有参装饰器(1,y=1,z=2)
def 被装饰对象(x,y):
time.sleep(3)
print('index %s %s' %(x,y))
装饰器的语法糖
装饰器语法糖,是属于装饰器的。
@:装饰器的的语法糖
注意: 在使用装饰器语法糖时,装饰器必须定义在被装饰对象之上。
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()
二、迭代器
定义:迭代取值的工具, 它可以迭代取值。
迭代:是指重复迭代,每一次迭代的结果都是基于上一次的结果而来的
-可迭代对象:所有序列类型: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)