在python中,可迭代对象与迭代器对象是非常重要的两大知识点,它们为开发人员处理序列数据提供了强大的工具和灵活性。
一、可迭代对象
可迭代对象指的是能够被迭代或者遍历的对象。在Python中,凡是实现了__iter__()
方法的对象都视为可迭代对象。
1.1 可迭代对象的特点
1.可以通过for...in循环进行遍历:可迭代对象可以使用for...in循环逐个访问其中的元素,遍历过程会自动调用可迭代对象的__iter__()方法来获取迭代器对象,并使用迭代器对象的__next__()方法来获取下一个元素。
2.可以使用iter()函数转换为迭代器对象:可迭代对象可以通过内置的iter()函数将其转换为迭代器对象,然后可以使用next()函数逐个访问其中的元素,直到遍历完所有元素。
3.可以使用in和not in操作符进行成员判断:可迭代对象可以使用in和not in操作符判断某个元素是否存在于对象中。
常见的可迭代对象包括(不限于)以下几种:
1.序列类型:列表(list)、元组(tuple)、字符串(str)、字节序列(bytes)、字节数组(bytearray)等。
2.集合类型:集合(set)、冻结集合(frozenset)、字典(dict)的keys、values等。
3.文件对象:通过open()函数打开的文件对象也是可迭代对象,通过迭代器每次读取文件的一行内容。
4.自定义类对象:自定义的类可以实现__iter__()方法,使其实例成为可迭代对象。
1.2 可迭代对象的自定义实现(自定义可迭代对象)
在上文中提到——凡是实现了__iter__()
方法的对象都视为可迭代对象。那么在实现自定义的可迭代对象时,需要在类中重写__iter__()
方法来进行创建。代码如下:
class MyIterable:
def __init__(self, data):
self.data = data
def __iter__(self):
return iter(self.data)
my_iterable = MyIterable([1, 2, 3, 4, 5])
for i in my_iterable:
print(i)
在上述示例中,定义类MyIterable
,在类中实现了__iter__()
方法。在该方法中通过内置函数iter()
函数将self.data
转换为迭代器对象,从而使得MyIterable
的示例称为了一个可迭代对象,通过for...in...
来循环遍历该实例中的元素。
二、迭代器对象
迭代器对象(iterator)是 Python 中用于遍历可迭代对象的工具。其具有以下特点:
1.实现了 __iter__() 方法和 __next__() 方法:迭代器对象必须定义 __iter__() 方法来返回自身,并实现 __next__() 方法来返回下一个元素,如果没有下一个元素则引发 StopIteration 异常。
2.记录迭代状态:迭代器对象会在每次调用 __next__() 方法时前进到下一个元素,并记录当前的状态,以供后续的迭代调用。
3.惰性计算:迭代器对象是一种惰性计算的机制,它在需要下一个元素时才进行计算,这样可以节省内存和计算资源。
4.只能往前遍历一次:一旦遍历到迭代器末尾并引发 StopIteration 异常,迭代器对象就不能再进行迭代了。如果需要重新遍历,需要重新创建一个新的迭代器对象。
迭代器对象可以使用内置的 iter()
函数来创建,该函数会调用可迭代对象的 __iter__()
方法并返回一个迭代器对象。我们可以使用 next()
函数来逐个获取迭代器对象中的元素。
下面是一个使用迭代器对象的示例:
# 创建一个迭代器对象
my_iter = iter([1, 2, 3, 4, 5])
# 使用 next() 函数获取迭代器中的下一个元素
print(next(my_iter)) # 输出: 1
print(next(my_iter)) # 输出: 2
print(next(my_iter)) # 输出: 3
# 使用 for...in 循环遍历迭代器对象,会自动处理 StopIteration 异常
for item in my_iter:
print(item) # 输出: 4, 5
在上述示例中,我们通过调用 iter()
函数将可迭代对象 [1, 2, 3, 4, 5]
转换为一个迭代器对象 my_iter
。然后,我们使用 next()
函数逐个获取迭代器中的元素。当遍历完所有元素后,再次调用 next()
函数会引发 StopIteration
异常。最后,我们使用 for...in
循环遍历迭代器对象时,它会自动处理 StopIteration
异常,避免了手动捕捉异常的麻烦。
同时,通过程序输出不难看出,循环中的print函数的输出是从第四个元素开始输出的,二者刚好对应了上方描述的迭代器对象的特点的第三和第四点。
三、生成器对象
生成器对象是由生成器函数返回的迭代器对象(其特性与迭代器对象相同,不再详述)。生成器函数使用 yield
语句产生值,并在每个值的产生之间暂停函数的执行,以便下次调用时可以从暂停的位置继续执行。接下来看生成器对象的实现方式:
def my_generator():
yield "Hello"
yield "World"
yield "Generator"
# 创建生成器对象
gen = my_generator()
# 使用循环遍历生成器对象
for item in gen:
print(item) # 输出: Hello, World, Generator
在上述示例中,我们定义了一个名为 my_generator
的生成器函数,并在函数体内使用 yield
语句逐个产生值。然后,我们通过调用生成器函数得到一个生成器对象 gen
。最后,我们使用 for
循环遍历生成器对象,它会自动迭代并打印生成器产生的值。
关于yield
yield是实现生成器的关键字,其用途就是用于定义一个生成器(也就是将一个函数转变成一个生成器对象),用于按需产生值的迭代器。yield
的功能如下:
代码在函数中执行遇到yield时,函数会在此处暂时执行并返回一个值给函数调用者。与return类似但不完全相同,代码执行遇到yield跳出函数之后,下一次调用函数时代码的执行是从上次调用跳出函数时的逻辑继续往下执行,例如一个循环中第一次循环yield了一个值,那么下次调用这个函数的时候就是继续这个循环的逻辑,也就是开始第二次循环。例如下方代码:
def my_generator():
for i in range(1, 6):
yield i
gen = my_generator()
print(next(gen)) # 输出: 1
print(next(gen)) # 输出: 2
print(next(gen)) # 输出: 3
for i in gen:
print(i) # 依次输出4和5
详细拆分的话为以下三点:
1.单个值的生成: 最常见的用法是在生成器函数中使用 yield 语句逐个生成值。每次调用生成器函数时,它会在 yield 语句处暂停,并返回生成的值。下次调用生成器对象的 next() 方法时,生成器会从上次暂停的位置继续执行,并产生下一个值。
def my_generator(): yield 1 yield 2 yield 3 gen = my_generator() print(next(gen)) # 输出: 1 print(next(gen)) # 输出: 2 print(next(gen)) # 输出: 3
2.值的传递: yield 语句可以作为表达式的一部分,将一个值发送给生成器对象。这可以用于在生成器函数的每次迭代中传递数据。
def my_generator(): value = yield "Ready" yield f"Received: {value}" gen = my_generator() print(next(gen)) # 输出: Ready print(gen.send("Hello, World")) # 输出: Received: Hello, World
在上述示例中,生成器函数首先返回字符串 “Ready”,然后通过
yield
语句等待下一个值的输入。接下来,我们通过调用gen.send("Hello, World")
方法向生成器对象发送了一个值,并在生成器函数中使用yield
语句将该值返回。3.迭代器耗尽: 在生成器函数中,当所有的 yield 语句都执行完毕时,生成器会自动引发 StopIteration 异常,标志着迭代的结束。这可以用于在迭代中终止生成器。
def my_generator(): yield 1 yield 2 yield 3 yield 4 gen = my_generator() for item in gen: print(item) # 输出: 1, 2, 3, 4 # 迭代结束后会引发 StopIteration try: next(gen) except StopIteration: print("Iteration ended")
在上述示例中,我们通过
for
循环迭代生成器对象中的值,当所有的值都产生完毕后,生成器会引发StopIteration
异常,我们可以捕获该异常以标志迭代的结束。