文章目录
一、迭代器的基本概念
迭代器是Python语言中的一个重要特性,用于遍历可迭代对象(如列表、元组、字典等)中的元素。Python中的很多内置对象都支持迭代器模式,可以通过iter()
函数获取一个迭代器对象,并使用next()
方法逐一访问其中的元素。
my_list = [1, 2, 3, 4, 5]
my_iterator = iter(my_list)
while True:
try:
value = next(my_iterator)
print(value)
except StopIteration:
break
# 首先使用iter()函数获取列表my_list的迭代器对象,并进入一个无限循环中,
# 逐一使用next()方法获取其元素并进行打印。当迭代器抛出StopIteration异常时,循环终止(结束)。
迭代器是一个定义了__iter__()
和__next__()
方法的对象。在Python中,很多内置对象都是可以被迭代的,例如列表、元组、字典、集合等。
当我们对一个可迭代对象使用iter()函数时,会得到一个迭代器对象。然后,就可以使用next()方法逐一访问该对象中的元素,直到迭代器抛出StopIteration异常为止。
通常情况下,我们不需要直接调用 iter() 方法来获取迭代器对象,而是通过使用 for 循环对可迭代对象进行遍历。当使用 for 循环语句时,如果要对一个对象进行迭代,程序会自动调用该对象的 iter() 方法,返回一个迭代器对象,然后使用迭代器对象进行迭代操作。
1.1 迭代器优点
-
更加高效:与传统的for循环相比,在大型数据集上使用迭代器可以减少内存消耗,节省系统资源;
-
更加灵活:迭代器允许我们以任意方式遍历数据集,包括正向、反向、跳跃等操作;
-
更加通用:几乎所有Python内置容器类型都是可迭代的,因此迭代器可以应用于各种不同的数据类型。
1.2 迭代器的编写方法
迭代器的编写方法非常简单,只需要定义一个支持__iter__()
和__next__()
方法的类即可。
__iter__ ()
方法返回迭代器对象本身,而__next__()
方法则返回下一个元素,在到达末尾时抛出StopIteration异常。
需要注意的是,对于无法提前预知迭代对象长度的情况,要在实现__next__()
方法时添加相应的终止条件。
class MyIterator:
def __init__(self, string):
self.string = string
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index == len(self.string):
raise StopIteration
result = self.string[self.index]
self.index += 1
return result
my_iterator = MyIterator("Hello, world!")
for char in my_iterator:
print(char)
自定义了一个迭代器类MyIterator,并在其中实现了__iter__()
和__next__()
方法。对于字符串类型的输入,__next__()
方法会逐一返回其中的字符,直到遇到结尾为止。
1.3 python内置迭代器函数
map(function, iterable)
:将一个函数应用于可迭代对象的每个元素,并返回一个新的迭代器对象,其中包含了应用后的结果;
filter(function, iterable)
:将一个函数应用于可迭代对象的每个元素,并返回一个新的迭代器对象,其中仅包含满足条件的元素;
zip(*iterables)
:将多个可迭代对象中相应位置的元素组合在一起,并返回一个新的元组迭代器对象;
reversed(iterable)
:翻转一个可迭代对象中的元素顺序,并返回一个新的迭代器对象。
my_list = [1, 2, 3, 4, 5]
new_list = list(map(lambda x: x ** 2, my_list))
print(new_list)
even_list = list(filter(lambda x: x % 2 == 0, my_list))
print(even_list)
my_tuple = ("apple", "banana", "cherry")
my_dict = {"name": "John", "age": 36, "country": "Norway"}
for item in zip(my_list, my_tuple, my_dict):
print(item)
for char in reversed("Hello, world!"):
print(char)
1.4 小结
迭代器是Python语言中的一个非常重要的特性,可以方便地遍历各种数据类型。
Python内置了很多支持迭代器模式的对象,同时也提供了一些常用的内置迭代器函数,包括map()、filter()、zip()和reversed()等。
在使用迭代器时,需要注意终止条件和异常处理等细节问题,以确保代码能够正确地遍历数据集。
1.5 迭代器对象与迭代对象
1.5.1 区别
1. 迭代对象
一个对象如果可以通过 for 循环进行遍历,则称其为迭代对象。
序列类型,如 list、tuple 和 string;
非序列类型,如 dict、set、file 等。
# 通过 __iter__() 方法可以获取一个迭代器对象,该方法在迭代对象中已经被实现。
# 换句话说,迭代对象 只存在 __iter__()
# 列表中调用 __iter__() 方法后,就可以得到一个迭代器对象
lst = [1, 2, 3]
it = lst.__iter__() # 获取列表的迭代器对象
print(dir(lst))
print(dir(it))
为了让一个对象可以被迭代,只需要确保其实现了 iter() 方法,即可满足迭代协议。当我们使用 for 循环遍历该对象时,Python 会自动调用其 iter() 方法,从而得到一个迭代器对象并进行迭代操作。
2. 迭代器对象
迭代器对象是一种可以逐个访问元素的对象,并且只能向前遍历。迭代器对象可以使用 next()函数获取下一个值,如果没有更多的元素,则会引发 StopIteration 异常。
迭代器对象必须包含以下两个方法:
__iter__()
方法返回迭代器对象本身;__next__()
方法返回下一个值,如果没有下一个值,则引发 StopIteration 异常。
我们可以使用普通函数或生成器函数来创建迭代器对象。例如,在 Python 中,我们可以使用 iter() 函数将可迭代对象转换为迭代器对象:
# 使用普通函数或生成器函数来创建迭代器对象
lst = [1, 2, 3]
it = iter(lst) # 将列表转换为迭代器对象
需要注意的是,一旦我们使用 next() 函数获取了迭代器对象中的某个元素,该元素就从迭代器对象中消失了。因此,迭代器对象只能用于一次遍历。
3. 小结
-
迭代器对象可以逐个访问元素,且只能向前遍历;
-
迭代对象可以通过
__iter__()
方法获取一个迭代器对象; -
迭代器对象必须包含
__iter__()
和__next__()
方法; -
迭代器对象只能用于一次遍历,而迭代对象不受此限制。
1.5.2 方法区分
Python的 collections.abc 模块中的 Iterable 和 Iterator 类来判断一个对象是否为迭代对象和迭代器对象
导入 collections.abc 模块中的 Iterable 和 Iterator 类。然后,定义一个列表 lst 和一个生成器 gen,并分别使用 isinstance() 函数和 Iterable 或 Iterator 类对其进行判断。
在输出结果中,可以看到,列表 lst 是一个迭代对象,但不是一个迭代器对象;而生成器 gen 同时属于迭代对象和迭代器对象。
注意:在判断一个对象是否为迭代器对象时,必须先保证它是一个迭代对象,否则会出现错误。
from collections.abc import Iterable, Iterator
lst = [1, 2, 3] # 列表属于迭代对象,但不是迭代器对象
gen = (i for i in range(5)) # 生成器属于迭代器对象,也属于迭代对象
print(isinstance(lst, Iterable)) # True
print(isinstance(gen, Iterable)) # True
print(isinstance(lst, Iterator)) # False
print(isinstance(gen, Iterator)) # True
二、生成器基本概念
生成器(generator)是 Python 中一种特殊的迭代器,它是一种函数或表达式,可以在运行时逐个产生值,并且只会在需要时进行计算。
与列表、元组等序列类型不同,生成器并不会一次性把所有元素计算出来并保存在内存中,而是按需生成每个值,从而节省了大量的计算资源和存储空间。
生成器创建:
生成器函数:通过使用 yield 语句将一个函数转换为生成器;
生成器表达式:类似于列表推导式,使用 (expr for var in iterable) 的形式来创建生成器对象。
1. 生成器函数
生成器函数是一种特殊的 Python 函数,它可以暂停执行并返回中间结果。当调用生成器函数时,它不会立即执行函数体中的所有代码,而是返回一个生成器(generator)对象。然后,我们可以使用 next() 或 send() 方法逐步迭代该生成器,并在需要时生成新值。
生成器函数可以使用 yield 语句来暂停函数执行并返回中间值。在函数执行期间,可以多次使用 yield 语句返回多个中间结果。每次调用生成器函数时,它都从上次停止的位置继续执行,并在遇到新的 yield 语句时返回相应的中间结果。
# 使用生成器函数创建斐波那契数列生成器
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# 创建一个 Fibonacci 数列生成器
fib = fibonacci()
# 生成前10个 Fibonacci 数字
for i in range(10):
print(next(fib))
定义了一个 fibonacci 函数,它使用 yield 语句暂停执行并返回每个斐波那契数列中的数字。然后将其赋值给 fib 变量,并使用 next() 函数依次获取它返回的每个中间值,并在循环中输出前10个 Fibonacci 数字。
send() 方法将值发送到生成器。send() 方法类似于 next() 方法,但它可以在生成器中传递一个值,并且该值会成为生成器中 yield 的表达式的结果。
def my_generator():
while True:
val = yield # 中间值接收
if val is not None:
print(f"Received value: {val}")
else:
print("No value received")
gen = my_generator()
next(gen) # 启动生成器
gen.send(10) # 向生成器发送值 10
gen.send("Hello") # 向生成器发送字符串 "Hello"
gen.send(None) # 向生成器发送空值
定义了一个 my_generator() 函数,它使用 while True 循环不断返回 yield 所产生的值,并在 yield 语句中使用变量 val 来接收 send() 方法所发送的值。通过调用 next() 方法启动生成器后,我们可以使用 send() 方法向其发送数据,从而在每次调用时产生新值。同时,我们还可以通过判断接收到的值是否为空来控制程序的行为。
使用 send() 方法将值发送到生成器,并让生成器在需要时返回相应的中间结果。与 next() 方法不同,send() 方法可以在生成器中接收一个值,并且该值会成为生成器中 yield 的表达式的结果。这种机制可以帮助我们更好地控制生成器的行为,从而实现更高效、更功能强大的程序。
2. 生成器表达式
生成器表达式是一种生成器构造形式,它类似于列表推导式,但是在语法上略有不同。
它们使用圆括号而不是方括号来括起来,并使用 (expr for var in iterable) 的形式来生成新元素,从而节省了大量的计算资源和存储空间。
# 使用生成器表达式创建一个数字生成器
gen = (i for i in range(5))
# 迭代生成器以产生数字
for num in gen:
print(num)
使用生成器表达式创建了一个数字生成器,它将生成从0到4的五个数字。然后使用 for 循环遍历该生成器并输出每个数字。
生成器是 Python 中一种强大的工具,它们可以逐个生成值,并节省计算和存储空间。通过使用 yield 语句定义生成器函数或使用生成器表达式,我们可以轻松地构建和使用生成器,减少计算和存储成本,提高程序效率。