迭代器
-
可迭代对象:可以使用for循环来遍历的,可以使用isinstance()来测试。
-
迭代器:同时实现了__iter__()方法和__next__()方法,可以使用isinstance()方法来测试是否是迭代器对象
from collections.abc import Iterable, Iterator
li = [11, 22, 33, 44, 55]
print(isinstance(li, Iterable)
iterator_li = iter(li)
print(isinstance(iterator_li, Iterator)
使用类实现迭代器
两个类实现一个迭代器
class MyList(object):
"""自定义的一个可迭代对象"""
def __init__(self):
self.items = []
def add(self, val):
self.items.append(val)
def __iter__(self):
#python中万物皆对象,将该类对象的引用self作为实参传入到MyIterator类中
myiterator = MyIterator(self)
return myiterator
class MyIterator(object):
"""自定义的供上面可迭代对象使用的一个迭代器"""
#这里的mylist就MyList对象
def __init__(self, mylist):
self.mylist = mylist
# current用来记录当前访问到的位置
#每一次调用for循环时,索引位置都从零开始
self.current = 0
def __next__(self):
if self.current < len(self.mylist.items):
item = self.mylist.items[self.current]
self.current += 1
return item
else:
raise StopIteration
def __iter__(self):
return self
一个类实现迭代器
class MyList(object):
"""自定义的一个可迭代对象"""
def __init__(self):
self.items = []
self.current = 0
def add(self, val):
self.items.append(val)
def __iter__(self):
#返回自身引用,自己本身就是一个迭代器对象
return self
def __next__(self):
if self.current < len(self.items):
item = self.items[self.current]
self.current += 1
return item
else:
#使用for循环第二次遍历时,self.current可以从0开始
self.current = 0
raise StopIteration
-
可迭代对象与迭代器的总结
-
一个具备了__iter__方法的对象,就是一个可迭代对象
-
当对一个可迭代对象调用iter()函数时,它会自动调用这个可迭代对象的__iter__方法,这个方法返回的对象当作迭代器
-
当对一个迭代器对象调用next()函数时,他会自动调用这个迭代器对象的__next__方法,这个方法返回想要的数据
-
迭代器一定是可迭代对象,可迭代对象不一定是迭代器
-
数据类型list、dict、str等是Iterable单不是Iterator,不过可以通过iter()函数获得一个Iterator对象
-
迭代器的应用
-
如果每次返回的数据值不是在一个已有的数据集合中,而是通过程序按照一定的规律计算生成的,那就不用再依赖一个已有的数据集合,也就是说不用再将所有要迭代的数据都一次性缓存下来,这样可以节省大量的存储(内存)空间。
class FeiboIterator(object):
"""斐波那契数列迭代器"""
def __init__(self, n):
# 斐波那数列值的个数
self.n = n
# 记录当前遍历的下标
self.index = 0
# 斐波那数列前面的两个值
self.num1 = 0
self.num2 = 1
def __next__(self):
"""被next()函数调用来获取下一个数"""
if self.index < self.n:
num = self.num1
self.num1, self.num2 = self.num2, self.num1 + self.num2
self.index += 1
return num
else:
raise StopIteration
def __iter__(self):
"""迭代器的__iter__返回自身即可"""
return self
if __name__ == '__main__':
fb = FeiboIterator(20)
for num in fb:
print(num, end=' ')
生成器
生成器的定义
-
使用了yield关键字的函数不再是函数,而是生成器。
-
yield关键字有两点作用:
-
保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起
-
将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return作用
-
可以使用next()函数让生成器从断点处继续执行,唤醒生成器(函数)
-
python3中的生成器可以使用return返回最终运行的返回值
-
生成器是这样一个函数,他记住上一次返回在函数体中的位置。对生成器函数的第二次(或第n次)调用跳转至函数中间,而上次调用的所有的局部变量都保持不变。生成器不仅记住了他的数据状态,生成器还记住了它在流程控制构造(在命令式编程中,这种构造不只是数据值)中的位置。
-
生成器的特点:
-
存储的是生成数据的方式(即算法),而不是存储生成的数据,因此节约内存。
生成器的使用
def create_points():
k = 2
b = 1
x = 0
while True:
y = k * x + b
#当使用send方法时,会将send方法中的参数赋给temp,send()方法与next()方法类似,都可以取生成器中的下一个元素。
temp = yield (x,y)
if temp:
k, b = temp
x = y
if __name__ == '__main__':
#不再是一个普通函数的调用,因为函数体内包含yield关键字,此时是生成一个生成器对象
g = create_points()
# print(next(g))
# print(next(g))
# print(g.send((3, 4)))
# print(next(g))
# print(next(g))
count = 0
for i in g:
if count > 3:
break
print(i)
count += 1
参考链接:
python生成器的原理和业务场景下的使用_for line in tqdm(f, desc='read sentence_cuts'):如果不-CSDN博客
装饰器
装饰器的定义
装饰器可以在不改变某函数结构和调用方式基础之上,为其增加新的功能,并且最大化复用新的功能。
装饰器的应用场景
为函数增加日志记录、登录校验、权限校验等,我们可以将这些功能写成一个装饰器,然后直接应用到相应需要改功能的函数中即可,可以保证对原代码和函数零侵入。
装饰器的本质是一个闭包函数
- 装饰器函数
def timing_decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
execution_time = end_time - start_time
print(f"{func.__name__} cost time: {execution_time:.4f} s")
return result
return wrapper
- 构造执行函数
@timing_decorator
def demo():
time.sleep(5)
执行demo函数时,会打印该函数执行时间为5s
参考文献:
python装饰器全解--包含实现原理及应用场景_装饰器实现原理-CSDN博客
闭包
什么是闭包
概念:一个函数可以引用作用域外的变量,同时也可以在函数内部进行使用的函数,称之为闭包函数。而这个作用域外的变量,称之为自由变量。这个自由变量不同于C++的static修饰的静态变量,也不同于全局变量,它是和闭包函数进行绑定的。
-
闭包函数是函数的嵌套,函数内还有函数,即外层函数嵌套一个内层函数
-
在外层函数定义局部变量,在内层函数通过nonlocal引用,并实现指定功能,比如计数
-
最后外层函数return内层函数
-
主要作用:可以变相实现私有变量的功能,即用内层函数访问外层函数内的变量,并让外层函数内的变量常驻内存
为什么要使用闭包
-
封装变量: 闭包允许你创建一个包含函数和其所在环境中变量的封闭空间。这样,你可以隐藏一些变量,使其不易被外部访问,起到一定的封装作用。
-
保持状态: 闭包函数可以保持其创建时的状态。这意味着你可以在函数外部改变闭包内部的变量,并且在多次调用闭包时保持这些变量的状态。
-
函数工厂: 闭包函数可以用作函数工厂,动态生成函数。这对于根据不同情况生成不同行为的场景很有用。
-
实现装饰器: 闭包函数常用于实现装饰器模式,通过在函数外层包装其他函数来增强其功能,而无需修改原始函数的代码。
-
实现私有变量和方法: 通过闭包,你可以模拟出类似于面向对象编程中的私有变量和方法,限制对内部实现的访问。
闭包的应用场景是什么
创建一个计数器,但又不想让外部直接访问计数变量
def create_counter():
count = 0 # 这个变量在闭包内
def counter():
nonlocal count # 使用 nonlocal 关键字声明 count 不是局部变量
count += 1
return count
return counter
# 创建一个计数器
my_counter = create_counter()
# 使用计数器
print(my_counter()) # 输出 1
print(my_counter()) # 输出 2
print(my_counter()) # 输出 3
在这个例子中,create_counter
函数返回了一个内部定义的 counter
函数。count
变量被封装在 counter
函数的闭包中,因此外部无法直接访问。每次调用 my_counter()
时,计数器会递增并返回当前计数值。
这样做的好处是,我们可以创建多个独立的计数器,它们各自维护自己的状态,而不会互相干扰。闭包在这里提供了一种轻量级的状态封装和隔离的机制。