geekdoc-python-zh/docs/pythonland/37.md

9.2 KiB
Raw Permalink Blame History

Python 迭代器:示例代码及其工作原理

原文:https://python.land/deep-dives/python-iterator

要理解什么是 Python 迭代器,您需要知道两个术语:迭代器和可迭代的:

Iterator

An object that can be iterated, meaning we can keep asking it for a new element until there are no elements left. Elements are requested using a method called __next__.

Iterable

An object that implements another special method, called iter. This function returns an iterator.

多亏了迭代器Python 有了非常优雅的 for 循环。迭代器也使理解成为可能。尽管理解所有的内部工作需要一些工作,但它们在实践中非常容易使用!

目录

Python 迭代器如何工作

如上所述Python 迭代器对象实现了一个函数,该函数需要携带确切的名称__next__。这个特殊函数一直返回元素,直到它用完了要返回的元素,在这种情况下,类型为StopIteration异常被引发。要获得一个迭代器对象,我们需要首先在一个可迭代对象上调用__iter__方法。

我们可以使用内置的 Python range 函数看到这一切,这是一个内置的 Python iterable。让我们做一个小实验:

>>> my_iterable = range(1, 3)
>>> my_iterator = my_iterable.__iter__()
>>> my_iterator.__next__()
1
>>> my_iterator.__next__()
2
>>> my_iterator.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

如你所见:

  • range 返回一个 iterable 对象,因为它有__iter__方法。
  • 我们调用函数并将它返回的迭代器赋给my_iterator
  • 接下来,我们开始重复调用__next__方法,直到到达范围的末尾并引发StopIteration异常。

当然,这不是你在实践中使用迭代器的方式。我只是在演示它们内部是如何工作的。如果您需要手动获取迭代器,请使用iter()函数。而如果需要手动调用__next__方法,可以使用 Python 的next()函数。

因为只有下一个函数,你只能用迭代器前进。没有办法重置迭代器(除了创建一个新的迭代器)或获取以前的元素。

为什么迭代器和可迭代对象是分开的对象?

迭代器和可迭代对象可以是独立的对象,但不是必须的。没有什么能阻止我们回到这里。如果愿意,可以创建一个既是迭代器又是可迭代的对象。你只需要同时实现__iter____next__

那么,为什么构建这种语言的聪明人决定拆分这些概念呢?这与保持状态有关。迭代器需要维护位置信息,例如指向内部数据对象(如列表)的指针。换句话说:它必须跟踪下一个要返回的元素。

如果 iterable 本身保持这种状态,您一次只能在一个循环中使用它。否则,其他循环会干扰第一个循环的状态。通过返回一个新的迭代器对象和它自己的状态,我们没有这个问题。这很方便,特别是当你使用并发性的时候。

内置 Python 迭代器

一旦你开始寻找,你会发现许多 Python 类型是可迭代的。以下是 Python 固有的一些可迭代类型:

还有一些被称为生成器的特殊类型的迭代。最突出的生成器示例是 range()函数,它返回指定范围内的项目。这个函数生成这些数字,而不需要在一个实际的列表中具体化它们。显而易见的优点是,它可以节省大量内存,因为它需要做的只是记录上一次迭代的次数,以计算下一项。

Thank you for reading my tutorials. I write these in my free time, and it requires a lot of time and effort. I use ads to keep writing these free articles, I hope you understand! Support me by disabling your adblocker on my website or, alternatively, buy me some coffee. It's much appreciated and allows me to keep working on this site!

如何使用 Python 迭代器

现在我们了解了迭代器的工作原理,让我们看看如何使用 Python 迭代器。你会很快发现它的感觉和外观都很自然,一点也不难!

for 循环中的迭代器

与其他编程语言不同,循环 要求可迭代。下面是我们迭代一个列表和一个字符串的两个例子:

>>> mystring = "ABC"
>>> for letter in mystring:
...     print(letter)
...
A
B
C
>>> mylist = ['A', 'B', 'C']
>>> for letter in mylist:
...     print(letter)
...
A
B
C

如您所见就可迭代性而言Python 字符串的行为与 Python 列表相同。

理解中的迭代器

就像 for 循环一样,理解也需要一个可迭代的对象:

>>> [x for x in 'ABC']
['A', 'B', 'C']
>>> [x for x in [1, 2, 3,4] if x > 2]
[3, 4]
>>>

迭代 Python 字典键

Python 字典是可迭代的,因此我们可以循环遍历字典的所有键。字典迭代器只返回键,不返回值。这里有一个例子:

>>> d = {'name': 'Alice', 'age': 23, 'country': 'NL' }
>>> for k in d:
...     print(k)
...
name
age
country

有时候看到有人用这个来代替:for k in d.keys()。虽然结果一样,但明显少了几分优雅。

迭代字典值

要迭代 Python 字典的值,可以使用 values()方法:

>>> for k in d.values():
...     print(k)
...
Alice
23
NL

迭代字典键

如果您想从字典中获得键和值,请使用items()方法。您可以将items()for 循环Python 列表理解一起使用:

>>> for k,v in d.items():
...     print(k, v)
...
name Alice
age 23
country NL
>>>
>>> # With a list comprehension and f-string
>>> [f'{k}: {v}' for k, v in d.items()]
['name: Alice', 'age: 23', 'country: NL']

将 Python 迭代器转换为列表、元组、字典或集合

可以使用list()函数将迭代器具体化为一个列表。同样,您可以使用set()函数将迭代器具体化为一个集合,或者使用tuple()函数将其具体化为一个元组:

>>> list(range(1, 4))
[1, 2, 3]
>>> set(range(1, 4))
{1, 2, 3}
>>> tuple(range(1, 4))
(1, 2, 3)

如果您有一个返回(键,值)元组的迭代器,您可以用dict()将其具体化。

用 Python 逐行读取文件

多亏了迭代器,用 Python 逐行读取文件非常容易:

with open('cities.txt') as cities:
    for line in cities:
        proccess_city(line)

open()函数返回一个 iterable 对象,可以在 for 循环中使用。如果你喜欢,可以阅读我的综合文章,其中有关于用 Python 处理文件的所有细节。

创建自己的 Python 迭代器

创建自己的迭代器没有什么魔力。我将用一个返回偶数的简单迭代器类来演示。

正如我们所了解的,我们需要实现 iternext。为了简单起见,我们将在一个单独的类中做这件事。我们用一个 iter 方法来完成这个任务,该方法只返回self。我们可以把它变成一个无限的迭代器。但是为了便于演示,我们将在通过数字 8 时引发一个StopIteration异常。

请记住,如果您以这种方式构建迭代器,您就不能在并发环境中使用它。在这种情况下,您应该在每次调用__iter__时返回一个新对象。

如果你需要复习,请先阅读我们关于类和对象的教程。

class EvenNumbers:
    last = 0

    def __iter__(self):
        return self

    def __next__(self):
        self.last += 2

        if self.last > 8:
            raise StopIteration

        return self.last

even_numbers = EvenNumbers()

for num in even_numbers:
    print(num)

如果运行这个程序,输出将是:

2
4
6
8

如果您愿意,可以交互式地玩这个例子:

https://crumb . sh/embed/vywqnj 476 au