-
引子
-
装饰器
-
装饰器语法糖
-
无参装饰模板
-
叠加多个装饰器
-
迭代器
-
生成器
-
装饰器
-
1、什么是装饰器
装饰器就是一个用来为被装饰对象添加新功能的工具
装饰器指的定义一个函数,该函数是用来为其他函数添加额外的功能
-
2、为何要用装饰器
在添加新功能的时候要遵循开放封闭的原则:一旦软件上线运行之后,应该对修改源代码封闭,对扩展功能开放
原则:
1、不修改函数内的源代码
2、不修改函数的调用方式
装饰器就是在遵循原则1和2的前提下,为被装饰对象添加上新功能
-
3、如何实现装饰器
函数装饰器分为:无参装饰器和有参装饰两种,二者的实现原理一样,
都是“函数嵌套+闭包+函数对象”的组合使用的产物
# 需求:在不修改index函数的源代码和调用方式的前提下为其添加统计运行时间的新功能
import time # 导入时间模块
def index(): # 一个函数
time.sleep(1) # 模拟index运行的一点时间 源代码
print('from index') # 源代码
index() # 调用方式
# 方案一:失败
# 问题:确实添加了新功能,但是修改了源代码
import time
def index():
start = time.time() # 统计开始时间
time.sleep(1)
print('from index')
stop = time.time() # 统计结束时间
print('run time is %s' %(stop - start))
index()
# 方案二:失败
# 问题:看似装饰器的效果实现了,但是引发了重复代码的问题
import time
def index():
time.sleep(1)
print('from index')
start = time.time()
index()
stop = time.time()
print('run time is %s' %(stop - start))
# 方案三:失败
# 问题:把要装饰的代码丢进函数里,为的是重复调用,但是把index写死了,就无法用上装饰器
import time
def index():
time.sleep(1)
print('from index')
def wrapper():
start = time.time()
index()
stop = time.time()
print('run time is %s' %(stop - start))
wrapper()
wrapper()
# 方案四:失败
# 问题: 把index函数名变成变量名func,要为wrapper函数的函数体代码传参数,
# 直接用参数传参,装饰对象虽然写活了,但只能装饰index
import time
def index():
time.sleep(1)
print('from index')
def wrapper(func):
start = time.time()
func() # 被装饰函数
stop = time.time()
print('run time is %s' %(stop - start))
wrapper(index)
# 方案五:====》闭包函数
import time
def index(): # index = 被装饰对象index函数的内存地址
time.sleep(1)
print('from index')
def outter(func): # func = 被装饰对象index函数的内存地址
def wrapper():
start = time.time()
func() # 被装饰对象index函数的内存地址
stop = time.time()
print('run time is %s' %(stop - start))
return wrapper # 千万别加括号
# outter=(被装饰对象index函数的内存地址)
index = outter(index) # outter-》返回wrapper函数的内存地址-》index = wrapper函数的内存地址
print(index)
index()
# 方案六:====》将wrapper伪装成被装饰的函数,name应该装的尽可能像一些
import time
def index(): # index = 被装饰对象index函数的内存地址
time.sleep(1)
print('from index')
def home(name):
time.sleep(2)
print("welcome %s to home page" %name)
return 123
def outter(func): # func = 被装饰对象index函数的内存地址
def wrapper(*args,**kwargs):
start = time.time()
res = func(*args,**kwargs) # 被装饰对象index函数的内存地址(x,y,z)
stop = time.time()
print('run time is %s' %(stop - strt))
return res
return wrapper
index = outter(index)
home = outter(home) # 偷梁换柱:home这个名字指向的wrapper函数的内存地址
res = home("geng") # wrapper("geng")
print(res)
res = index() # wrapper()
print(res)
-
装饰器语法糖
语法糖:让你开心的语法
import time
from functools import wraps
def outter(func): # func = 被装饰对象index函数的内存地址
@wraps(func)
def wrapper(*args,**kwargs):
start = time.time()
res = func(*args,**kwargs) # 被装饰对象index函数的内存地址(x,y,z)
stop = time.time()
print('run time is %s' %(stop - start))
return res
return wrapper # 千万别加括号
# 在被装饰对象正上方的单独一行写@装饰器名字
@outter # index = outter(index) # outter(被装饰对象index函数的内存地址) -> 返回wrapper
# 函数的内存地址---> index = wrapper函数的内存地址
def index(): # index = 被装饰对象index函数的内存地址
"""这是index函数"""
time.sleep(1)
print('from index')
@outter # home = outter(home)
def home(name):
time.sleep(2)
print("welcome %s to home page" %name)
return 123
# res = home("egon") # wrapper("egon")
# print(res)
# res = index() # wrapper()
# print(res)
print(index.__name__)
print(index.__doc__)
-
无参装饰器模板
# 闭包函数 函数 (被装饰函数)
def outter(func):
def wrapper(*args,**kwargs): # wrapper函数,在wrapper内写被装饰的函数
res = func(*args,**kwargs) # *args,**keargs让参数保持一致,装的像一点
return res # 让返回值保持一致,装的像一点
return wrapper
# 以上的装饰器为被装饰对象啥新功能也没添加,就是无参装饰器模板
# 认证功能装饰器模板
def login(func):
def wrapper(*args,**kwargs):
name = input('username>>>: ').strip()
pwd = input('password>>>: ').strip()
if name == 'egon' and pwd == "123":
res = func(*args,**kwargs)
return res
else:
print('账号密码错误')
return wrapper
@login
def index():
print('index')
index()
-
叠加多个装饰器
def deco1(func1): # func1 = wrapper2的内存地址
def wrapper1(*args,**kwargs):
print('==========>wrapper1')
res1 = func1(*args,**kwargs)
print('end1')
return res1
return wrapper1
def deco2(func2): # func2 = wrapper3的内存地址
def wrapper2(*args,**kwargs):
print('=========>wrapper2')
res2 = func2(*args,**kwargs)
print('end2')
return res2
return wrapper2
def deco3(func3): # func3 = 被装饰index的内存地址
def wrapper3(*args,**kwargs):
print('=========>wrapper3')
res3 = func3(*args,**kwargs)
print('end3')
return res3
return wrapper3
# index = wrapper1的内存地址
@deco1 # deco1(wrapper2的内存地址) -> wrapper1的内存地址
@deco2 # deco2(wrapper3的内存地址) -> wrapper2的内存地址
@deco3 # deco3(被装饰index的内存地址)-> wrapper3的内存地址
def index():
print('index......')
index()
-
迭代器
-
1、什么是迭代器
迭代器指的是迭代取值的工具
什么是迭代?
迭代就是一个重复的过程,但是每一次重复都是在上一次的基础之上进行的,单纯的重复不是迭代
nums = [111, 222, 333] # 遍历列表,索引,迭代取值
nums = "hello" # 遍历字符串,索引,迭代取值
def get(l): # 函数就是取值工具
i = 0
while i < len(l):
print(l[i])
i += 1
get(nums)
-
2、为何要用迭代器
1、迭代器提供了一种不依赖于索引的、通用的迭代取值方案
2、节省内存
-
3、如何用迭代器
可迭代的对象
1、内置有_iter_方法的对象都叫可迭代的对象
调用可迭代对象的_iter_方法,返回的是它的迭代器
迭代器对象
1、内置有_next_方法
2、内置有_iter_方法
调用迭代器对象的_iter_方法得到的它自己,就跟没调用一样
调用迭代器对象的_next_方法返回下一个值,不依赖索引
可以一直调用_next_直到取干净,则抛出异常StopIteration
# 内置的类型都是可迭代的对象
"abc" # 字符串
[1,2,3] # 列表
(1,2,3) # 元组
{'k1':111} # 字典
{1,2,3} # 集合
f = open('a.txt',mode='wt') # 文件对象本身就是迭代器对象
# 例1:
# 迭代器直接取值方式
nums = [111,222,3333]
nums_iter = nums.__iter__() # 将可迭代对象转为迭代器版本,源列表没动,产生新的
print(nums_iter)
print(nums_iter.__next__())
print(nums_iter.__next__())
print(nums_iter.__next__())
print(nums_iter.__next__()) # 抛出异常 StopIteration
x = iter(nums)
print(next(x))
print(next(x))
print(next(x))
# 例2:
# 用while循环解决迭代器取值
nums = [111,222,3333]
nums_iter = iter(nums) # 把可迭代对象先转成迭代器对象
while True:
try: # 异常处理,next到第四次检测出异常
res = next(nums_iter)
print(res)
except StopIteration: # 捕捉异常,结束循环
break
# 1、先调用in后那个对象的__iter__方法,拿到迭代器对象
# 2、 res = next(迭代器对象),执行一次循环体代码
# 3、循环往复步骤2,直到值取干净抛出异常StopIteration,for会捕捉异常结束循环
nums = [111,222,3333] # 用for循环两行代码解决例2
for res in nums:
print(res)
-
迭代器的优缺点
优点:
1、为序列和非序列类型提供了一种统一的迭代取值方式。
2、惰性计算:迭代器对象表示的是一个数据流,可以只在需要时才去调用next来计算出一个值,就迭代器本身来说,同一时刻在 内存中只有一个值,因而可以存放无限大的数据流,而对于其他容器类型,如列表,需要把所有的元素都存放于内存中,受内 存大小的限制,可以存放的值的个数是有限的。缺点
缺点:
1、除非取尽,否则无法获取迭代器的长度
2、只能取下一个值,不能回到开始,更像是‘一次性的’,迭代器产生后的唯一目标就是重复执行next方法直到值取尽,否则就会 停留在某个位置,等待下一次调用next;若是要再次迭代同个对象,你只能重新调用iter方法去创建一个新的迭代器对象,如 果有两个或者多个循环使用同一个迭代器,必然只会有一个循环能取到值。
-
生成器
-
yield 与 return的异同
相同点:返回值层面用法一样
不同点:return只能返回值一次,而yield可以返回多次
def func():
print('xxx')
yield 111
print('yyy')
yield 222
print('zzz')
yield 333
print('mmmm') # 报错,抛出异常 StopIteration 取到这里就取干净了
# 当函数内出现yield关键字,再调用函数并不会触发函数体代码的运行,会返回一个生成器
g = func()
# print(g) # 生成器就是一种自定义的迭代器
res = next(g)
print(res)
res = next(g)
print(res)
res = next(g)
print(res)
next(g)
# 例:
# 一出场就是一只老母鸡,可以存无穷个值的类型,但每次只能拿一个
def func():
res = 1
while True:
yield res
res += 1
g = func() # 得到一个迭代器
print(next(g)) # 打印next触发代码运行,可以返回无数个值
print(next(g))
print(next(g))
nums =[111,22,33]
x = iter(nums)
for res in x: # x.__iter__()
print(res)
print('='*50)
for res in x: # x.__iter__()
print(res)
for res in nums: # nums.__iter__()
print(res)
print('='*50)
for res in nums: # nums.__iter__()
print(res)