文章目录
- 1. 生成器
- 2. 迭代器
迭代器
用于从集合中取出元素,生成器
用于凭空生成元素;iter()方法可以获取一个迭代器(_iter__或_getitem__);如果实现了__iter__方法,对象就是可迭代的;使用iter(a)可判断a是否可迭代,不可迭代会报错(或isinstance(a, abc.iterable) (from collections import abc));可迭代对象与迭代器关系:Python从可迭代的对象中获取迭代器
1. 生成器
生成器:根据程序制定的规则循环生成数据,当条件不成立时则生成数据结束。数据不是一次性全部生成出来,而是使用一个,再生成一个,可以节约大量的内存
。创建生成器的方式:生成器推导式、yield 关键字
比如如下示例,使用常规方法实现如下:
codes = []
for i in range(10):codes.append(i)
print(codes) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
也可以使用列表推导方式,列表推导可以把一个序列或其他可迭代类型中的元素过滤或加工,然后再创建一个新的列表,python内置函数filter和map组合使用可得到同样效果,但可读性不如列表推导,列表推导一般只用来创建新的列表,并且尽量保持简短,如果超过两行,要考虑是否得用for循环重写
codes = [i for i in range(10)] # # 以前的版本(如Python2.7)使用列表推导时同名变量会把原变量替代,如存在全局变量i,则全局变了i会被改变
# 如果获取偶数则codes = [i for i in range(10) if i % 2 == 0]
print(codes) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
如果获取偶数列表,则后面加个if条件
codes = [i for i in range(10) if i % 2 == 0]
print(codes) # [0, 2, 4, 6, 8]
也可以使用filter和map组合实现
codes = list(filter(lambda x: x % 2 == 0, range(10)))
print(codes) # [0, 2, 4, 6, 8]
生成器表达式遵守迭代器协议,可以逐个产生元素,而不是先建立一个完整的列表,然后把这列表传递到某个构造函数里。使用生成器表达式可以节省内存。生成器表达式的语法与列表推导类似,只不过把括号改成圆括号,代码如下
codes = (i for i in range(10))
print(codes) # <generator object <genexpr> at 0x000001F65612E5C0>
for i in codes: # 循环输出值,自动处理异常print(i, end=" ") # 0 1 2 3 4 5 6 7 8 9
# 也可以使用while代替i,但是要自己处理异常
# while True:
# try:
# value = next(codes)
# print(value, end=" ") # 0 1 2 3 4 5 6 7 8 9
# except Exception as e:
# break
利用生成器表达式生成笛卡尔积
colors = ['black', 'white']
sizes = ['S', 'M', 'L']
tshirts = list((color, size) for color in colors for size in sizes)
print(tshirts) # [('black', 'S'), ('black', 'M'), ('black', 'L'), ('white', 'S'), ('white', 'M'), ('white', 'L')]
yield 关键字:只要在def函数里面看到有 yield 关键字那么就是生成器,yield关键字的作用是将这个函数变成一个生成器对象。执行时,解释器遇到 yield 后会中断代码的执行,并返回yield后的数据,下一次再执行时,会恢复前面yield中断的状态,继续执行
def get_value(n):for i in range(n):yield ig = get_value(10)
print(g)
print(type(g))# 循环输出值,最后一个之后会出现异常,用try处理
# while True:
# try:
# value = next(g)
# print(value, end=' ')
# except Exception as e:
# print(e)
# break
# 循环输出值,自动处理异常
for i in g:print(i, end=' ')
2. 迭代器
迭代器存在的目的是为了管理for语句;上下文管理器对象存在的目的是管理with语句;with语句的目的是简化try/finally模式,这种模式用于保证一段代码运行完毕后执行某项操作,即便那段代码由于异常、return或sys.exit()调用而终止也会执行制定的操作,finally子句中的代码通常用于释放重要的资源,或者还原临时变更的状态;上下文管理器协议包含__enter__和__exit__,with开始执行时会在上下文管理器对象上调用__enter__方法,结束后在上下文管理器对象上调用__exit__
with open("a.txt") as f:src = f.read(60)
print(len(src))
print(f)
print(f.closed, f.encoding)
print(f.read(60)) # 不能执行I/O操作,因为在with块的末尾把文件关闭了
字符串abc是可迭代的对象,背后是有迭代器的,只不过看不到
a = "abc"
for i in a: # iter(a)print(i)
如果没有for语句,那使用while模拟实现
a = "abc"
it = iter(a) # 构建迭代器
while True:try:print(next(it)) # 不断在迭代器上调用next函数,获取下一个字符except StopIteration: # 没有字符抛出异常(迭代器到头了,for会处理该异常)del it # 释放对it的引用(废弃迭代器对象)break
标准的迭代器接口有两个:__next__返回下一个可用的元素,没有则抛出StopIteration异常;__iter__返回self,以便在使用可迭代对象的地方使用迭代器(如for循环中)
a = iter("abc")
print(a)
print(next(a))
print(next(a))
print(list(a))
print(list(iter("abc")))
迭代器没有方法检验是否还有遗留的元素,也没有办法还原已耗尽的迭代器,如果想再次迭代,需从新构造迭代器
典型的迭代器
class A:def __init__(self, words):self.words = wordsself.index = 0def __next__(self):try:word = self.words[self.index]except IndexError:raise StopIteration()self.index += 1return worddef __iter__(self):return selfa = A("abc")
print(a)
print(next(a))
print(next(a))
print(next(a))
可迭代的对象要有__iter__方法,每次都实例化一个新的迭代器;迭代器需要实现__next__方法,返回单个元素,此外还要实现__iter__方法,返回迭代器本身
利用生成器函数
import reprlibclass A:def __init__(self, words):self.words = wordsdef __repr__(self):return "A(%s)" % reprlib.repr(self.words)def __iter__(self):for word in self.words:yield wordreturna = A("abc")
print(a)
for i in a:print(i)
迭代器是生成器对象,每次调用__iter__方法都会自动创建,这里的__iter__方法是生成器函数
生成器函数的工作原理
只有python函数的定义体中有yield关键字,该函数就是生成器对象,调用生成器函数时,会返回一个生成器对象,生成器函数是生成器工厂
下面是一个简单的函数说明生成器的行为
def ad():yield 1yield 2yield 3print(ad)
print(ad())
for i in ad():print(i)
a = ad()
print(next(a))
print(next(a))
print(next(a))# <function ad at 0x0000026F441304A0>
# <generator object ad at 0x0000026F557B4720>
# 1
# 2
# 3
# 1
# 2
# 3
生成器函数会创建一个生成器对象,包装生成器函数的定义体
__iter__方法是生成器函数调用时会构建一个实现了迭代器接口的生成器对象
上面的这些实例都在__init__方法中构建好了字符串,绑定到了self.words属性上,没起到节约空间的作用
re.finditer函数是re.findall函数的惰性版本,返回的不是列表,而是一个生成器,相比之下,re.finditer能节省大量内存
import re
import reprlibRE_WORD = re.compile('\w+')class A:def __init__(self, text):self.text = textdef __repr__(self):return 'A(%s)' %reprlib.repr(self.text)def __iter__(self):for match in RE_WORD.finditer(self.text):yield match.group()