python—生成器、迭代器、装饰器
迭代器
- 可迭代对象
- 可以更新迭代的 实实在在的值。
- 内部含有’_iter()'方法的。
- str list tuple sict set range
- 优点:操作方法多,操作灵活,直观。
- 缺点:占用内存。
- 迭代器
- 可以更新迭代的一个工具(数据结构)。
- 内部含有iter() 且含有 __next(0)_方法的。
- 文件句柄。
- 优点:节省内存。惰性机制。
- 缺点:不直观,速度相对慢,操作方法单一,不走回头路。
# 获取一个对象的所以方法:dir()
s1 = 'sdsdasasdas'
print(dir(s1))
l1 = [1,2,3]
print(dir(l1))
print('__iter__' in dir(l1)) # True
-
判断一个对象是否是可迭代对象
-
小结
字面意思:对象?python中一切皆对象。一个实实在在存在的值,对象。
专业角度:可迭代对象?内部含有”iter“方法的对象,可迭代对象。
优点:1.存储的数据直接能显示,比较直观。2.拥有的方法比较多,操作很方便。
缺点:1.占用内存。2.不能直接通过for循环,不能直接取值。 -
迭代器
-
迭代器的定义
字面意思:更新迭代,器:工具:可更新迭代的工具。
专业角度:内部含有内部含有__iter__
方法并且含有__next__
方法的对象就是迭代器。
可以判断是否是迭代器:有__iter__
and__next__
方法在不在dir(对象)
文件句柄with open('wenjian', encoding='utf-8', mode='w') as f1: print('__iter__' in dir(f1) and '__next__' in dir(f1))
输出True。 -
判断一个对象是否是迭代器
-
迭代器的取值
s1 = 'dasdasdas'
obj = iter(s1) # s1.__iter__()
# print(obj) # <str_iterator object at 0x0000022770E5EB70>
print(next(obj)) # d
print(obj.__next__()) # a
l1 = [11,22,33,44,55]
obj = l1.__iter__()
print(obj.__next__())
print(obj.__next__())
print(obj.__next__())
print(obj.__next__())
print(obj.__next__())
- 可迭代对象如何转化成迭代器
- while循环模拟for循环机制
# while循环模拟for循环机制
l1 = [1,2,3,4,5,6,7,8]
# for i in l1: # 先转换成迭代器
# print(i)
# 将可迭代对象转换成迭代器
# StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 __next__() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。
obj = l1.__iter__()
while True:
try:
print(next(obj))
except StopIteration:
break
生成器
-
生成器
- 什么是生成器?:python社区,生成器与迭代器看成是一种。生成器的本质就是迭代器。唯一的区别:生成器是我们自己用python代码构建的数据结构。迭代器都是提供的,或者转化得来的。
- 获取生成器的三种方式:
- 生成器函数。
- 生成器表达式。
- python内部提供的一些。
- 获取生成器的三种方式:
- 生成器函数获得的生成器。
- yield
- yeild return
- yeild from
- 什么是生成器?:python社区,生成器与迭代器看成是一种。生成器的本质就是迭代器。唯一的区别:生成器是我们自己用python代码构建的数据结构。迭代器都是提供的,或者转化得来的。
-
生成器表达式,列表推导式
- 用一行代码去构建一个比较复杂有规律的列表。
- 列表推导式:
- 循环模式:[变量(加工后的变量) for 变量 in iterable]
- 筛选模式:[变量(加工后的变量) for 变量 in iterable if 条件]
- 循环模式
# 函数
def func():
print(11)
print(22)
return 3
ret = func()
print(ret)
# 生成器函数
def func():
print(11)
print(22)
yield 3 # 后边写一个next函数就执行到这里,再写一个就接着执行到下一个yeild
a = 1
b = 2
c = a + b
print(c)
yield 4
func() # 不执行
print(func)
ret = func()
print(ret) # <generator object func at 0x000001E6D1DB9780>
print(next(ret)) # 一次返回一个yield值
print(next(ret))
# 一个next对应一个yield
return 和 yeild 有什么区别
return:函数中只存在一个return结束函数,并且给函数的执行者饭hi之。
yeild:只要函数中有yield那么它就是生成器函数而不是函数了。
生成器函数中可以存在多个yield,yield不会结束生成器函数,一个yield对应一个next(),
# 吃包子
# 这种一下就把2000个包子存进内存
def func():
l1 = []
for i in range(1, 5001):
l1.append(f'{i}号包子')
return l1
ret = func()
print(ret)
# 生成器方式
def gen_func():
for i in range(1, 5001):
yield f'{i}号包子'
ret = gen_func()
# next一次产生一个 极大节省内存
# 只产生一个数据集里边只有一个包子,这个被拿走下一个放进去。
# 需要200个包子
for i in range(200):
print(next(ret))
# 再要200个包子 从201开始
for i in range(200):
print(next(ret))
# yield from 将l1这个列表变成了迭代器返回
def func():
l1 = [1,2,3,4,5]
yield from l1 # 将l1这个列表变成了迭代器返回
"""
yield 1
yield 2
yield 3
yield 4
yield 5
"""
ret = func()
print(next(ret)) # 得到列表的第一个值
print(next(ret))
print(next(ret))
print(next(ret))
print(next(ret))
def func():
list1 = ['都是', '打散', '的撒', '离开']
list2 = ['空', '发帖人', '解耦i', '同意']
yield from list1
yield from list2
g = func()
for i in range(8):
print(next(g))
# for i in g:
# print(i)
装饰器
闭包
# 封闭的东西:保证数据的安全。
# 方案1
l1 = [] # 全局变量
li = []
def maker_average(new_value):
l1.append(new_value)
return sum(l1) / len(l1)
print(maker_average(100000))
print(maker_average(110000))
# 很多代码
l1.append(666) # 误用l1
print(maker_average(120000))
print(maker_average(90000))
# 方案2:数据安全,l1不能说全局变量。
# 每次执行时l1的列表都会重新赋值为空列表。
li = []
def maker_average(new_value):
l1 = []
l1.append(new_value)
return sum(l1) / len(l1)
print(maker_average(100000))
print(maker_average(110000))
# 很多代码
print(maker_average(120000))
print(maker_average(90000))
# 方案3:闭包。
def maker_average():
l1 = []
def average(new_value):
l1.append(new_value)
return sum(l1) / len(l1)
return average
avg = maker_average() # average
# 查看自由变量,存在即为闭包
print(avg.__code__.co_freevars) # ('l1',)
print(avg(100000))
print(avg(110000))
print(avg(120000))
print(avg(90000))
# 全局
# maker_average:地址1
# avg: 地址2
# 临时名称空间
# l1 = []
# average: 地址2
# 闭包: 多用于面试题:什么是闭包?闭包有什么作用?
# 1. 闭包只能存在嵌套函数中。
# 2. 内层函数对外层函数非全局变量的引用(使用),就会形成闭包。
# 被引用的非全局变量也称作自由变量,这个自由变量就会与内层函数产生一个绑定关系,
# 自由变量不会在内层中消失。
# 闭包的作用:保证程序的安全性。
# 如何判断一个嵌套函数是不是闭包?
# 1. 闭包只能存在嵌套函数中。
# 2. 内层函数对外层函数非全局变量的引用(使用),就会形成闭包。
# 例一:
def wrapper():
a = 1
def inner():
print(a)
return inner
ret = wrapper()
# 是
# 例二:
a = 2
def wrapper():
def inner():
print(a)
return inner
ret = wrapper()
# 不是
#
# # 例三:
#
def wrapper(a,b):
def inner():
print(a)
print(b)
return inner
a = 2
b = 3
ret = wrapper(a,b)
# 是
# 如何用代码判断闭包
def wrapper(a,b):
def inner():
print(a)
print(b)
return inner
a = 2
b = 3
ret = wrapper(a,b)
print(ret.__code__.co_freevars) # ('a', 'b') 有两个自由变量
装饰器的推导
# 装饰器:装饰,装修,房子就可以住,如果装修,不影响你住,而且体验更加,
# 让你生活中增加; 很多功能,洗澡,看电视
# 器:工具。
# 开放封闭原则:
# 开放:对代码的扩展是开放的。
# 封闭:对源码的修改是封闭的。
# 装饰器:完全遵循开放封闭原则。
# 装饰器:在不改变原函数代码以及调用方式的前提下给其增加新的功能。
# 装饰器就是一个函数。
#
# 版本一:A 写一些代码,测试一下index函数的执行效率。
def index():
"""有很多代码"""
time.sleep(2) # 模拟的网络延迟
print('欢迎登录博客园首页')
def dariy():
"""有很多代码"""
time.sleep(3) # 模拟的网络延迟
print('欢迎登录博客园日记首页')
# # 如果测试别人的代码,要重复复制
import time
# print(time.time()) # 格林尼治时间。 1599562276.1415439
star_time = time.time()
index()
end_time = time.time()
print(end_time - star_time)
# 版本2:利用函数,解决代码重复使用问题
import time
def index():
"""有很多代码"""
time.sleep(2) # 模拟的网络延迟
print('欢迎登录博客园首页')
def dariy():
"""有很多代码"""
time.sleep(3) # 模拟的网络延迟
print('欢迎登录博客园日记首页')
def timmer(f):
star_time = time.time()
f()
end_time = time.time()
print(end_time - star_time)
timmer(index)
# 版本二还是有问题:原来index函数源码没有改变,给原函数添加了一个新的功能,
# 测试原函数效率的功能。满足开放封闭原则?原函数的调用方式改变了
# 版本三:不能改编原函数的调用方式。
import time
def index():
"""有很多代码"""
time.sleep(2) # 模拟的网络延迟
print('欢迎登录博客园首页')
def dariy():
"""有很多代码"""
time.sleep(3) # 模拟的网络延迟
print('欢迎登录博客园日记首页')
def timmer(f):
def inner():
star_time = time.time()
f()
end_time = time.time()
print(end_time - star_time)
return inner
# timmer(index) # index()
# ret = timmer(index) # inner
# ret() # inner()
"""最基础的装饰器"""
index = timmer(index) # inner
index() # inner()
def func():
print('in func')
def func1():
print('in func1')
func()
func = 666
func() # TypeError: 'int' object is not callable
# 版本四:继续研究
import time
def index():
"""有很多代码"""
time.sleep(2) # 模拟的网络延迟
print('欢迎登录博客园首页')
def timmer(f):
# f = index
def inner():
star_time = time.time()
f()
end_time = time.time()
print(end_time - star_time)
return inner
index = timmer(index)
index()
# 版本五:python做了一个优化:提出了一个语法糖的概念。
# @装饰器名
# 标准版的装饰器
# import time
# # 装饰器写在最上面
# # timmer装饰器
def timmer(f):
# f = index
def inner():
star_time = time.time()
f()
end_time = time.time()
print(end_time - star_time)
return inner
@timmer # index = timmer(index)
def index():
"""有很多代码"""
time.sleep(0.6) # 模拟的网络延迟
print('欢迎登录博客园首页')
return 666
@timmer # dariy = timmer(dariy)
def dariy():
"""有很多代码"""
time.sleep(0.3) # 模拟的网络延迟
print('欢迎登录博客园日记首页')
# index = timmer(index)
# index()
# dariy = timmer(dariy) @timmer
# dariy()
ret = index()
print(ret) # None 有返回值 不行啦?
dariy()
# 版本六:被装饰函数有返回值
import time
# 装饰器写在最上面
# timmer装饰器
def timmer(f):
# f = index
def inner():
star_time = time.time()
r = f() # 把返回值给他返出去再
# print(f'真正的index函数返回值{f()}') # index()
end_time = time.time()
print(end_time - star_time)
return r
return inner
@timmer # index = timmer(index)
def index():
"""有很多代码"""
time.sleep(0.6) # 模拟的网络延迟
print('欢迎登录博客园首页')
return 666
# 加上装饰器不应该改变原函数的返回值,所以666应该返回给我下面的ret
# 但是下面这个ret实际接收的是inner函数的返回值,而666返回给的是装饰器里面的
# f(),也就是r,我们现在要解决的问题就是将r给inner的返回值。
ret = index()
print(ret)
# 版本七: 最标准版的装饰器。
import time
# 装饰器写在最上面
# timmer装饰器
def timmer(f):
# f = index
def inner(*args, **kwargs):
# 函数的定义:* 聚合 args=('李素琴', 18)
star_time = time.time()
r = f(*args, **kwargs) # 把返回值给他返出去再
# 函数的执行:* 打散:f(*iterable) --> f(*('李素琴', 18)) --> f('李素琴', 18)
# print(f'真正的index函数返回值{f()}') # index()
end_time = time.time()
print(end_time - star_time)
return r
return inner
@timmer # index = timmer(index)
def index(name):
"""有很多代码"""
time.sleep(0.6) # 模拟的网络延迟
print(f'欢迎{name}登录博客园首页')
return 666
@timmer # dariy = timmer(dariy)
def dariy(name, age):
"""有很多代码"""
time.sleep(0.3) # 模拟的网络延迟
print(f'欢迎{age}岁的{name}登录博客园日记首页')
dariy('李素琴', 18) # inner(name, age)
index('那亲') # inner('那亲')
# 标准版的装饰器
def wrapper(f):
def inner(*args, **kwargs):
"""添加额外的功能:执行被装饰函数之前的操作"""
ret = f(*args, **kwargs)
"""添加额外的功能:执行被装饰函数之后的操作"""
return ret
return inner