Python语言最令人印象深刻的功能之一就是使用了“ for-each”循环结构,自从我开始使用Python以来,这种结构就令我敬畏。 对于初学者,这是一个简单的for循环,它打印前10个自然数:
for num in range(1, 11):
print(num)
我们还可以通过类似的方式遍历基本类型,例如列表,元组,字典和字符串 :
numbers = [1, 2, 3, 4, 5]
record = ('Kshitij', 21, 'Loves Python')
details = {
'name': 'Kshitij',
'age': 21
}
for num in numbers:
print(num) # 1 2 3 4 5
for data in record:
print(data) # Kshitij 21 Loves Python
for key, value in details.items():
print(key, value) # age 21 name Kshitij
当一个人使用class
在Python中实现很少的数据结构时,他感到渴望遍历存储在其实例中的数据。 这就是迭代器协议的用武之地。
样例实施
让我们假设我们的任务是实现标准的52张卡片组 。 一个示例实现可能看起来像这样:
关于创建Deck
新实例并对其进行表示,这可以很好地工作。 但是,此实现中的一个主要难题是缺乏迭代Deck
对象的能力。
>>> from cards import Deck
>>> new_deck = Deck() # New deck instantiated
>>> print(new_deck)
... # Works great
>>> for card in new_deck:
... print(card)
TypeError: 'Deck' object is not iterable
可以足够聪明地探索实例new_deck
并得出结论, cards
属性包含迭代所需的数据,而它实际上是一个list
。 有了这些知识,他可以按如下方法破解上述循环:
>>> for card in new_deck.cards:
... print(card)
Card(...)
..
..
这段代码很棒。 但是,最终用户必须获得有关实现的内部信息才能执行迭代。 这使我们的代码失去了数据抽象的优势,并导致了很多实现上的期望。
肯定有更好的办法!
在Raymond Hettinger的热情推动下,我寻找了一些方法来改进我的实现,以配合Python的for
循环。
很快我找到了答案- 迭代器协议。
迭代器协议
为了了解协议是什么以及如何在Python中实现它,我们需要了解一些基本术语。
可迭代的
- 您可以使用for循环遍历任何对象。
- 可迭代的对象并不总是可索引的,它们并不总是具有长度,也不总是有限的。
- 可以将iterable传递给
iter()
内置函数以为其获取迭代器。
迭代器
- 迭代器只有一项工作:在迭代器中返回“ next”项。
- 可以将迭代器传递给内置的
next
函数,以从中获取下一个项;如果没有下一个项(因为我们到达末尾),则会引发StopIteration
异常。 - 迭代器在传递给内置的
iter()
时返回自身。
协议书
步骤01:内置iter()
的工作方式是什么?
每当解释器需要遍历对象x
,它将自动调用iter(x)
。 iter
内置函数:
- 检查对象是否实现
__iter__
方法,并调用该方法以获得迭代器。 - 如果未实现
__iter__
方法,但实现了__getitem__
方法,则Python将创建一个迭代器 ,尝试从索引0
开始按顺序获取项目。 - 如果失败,Python会引发
TypeError
异常,指出<classname> object is not iterable
。
步骤02:如何执行协议?
我将介绍两种实现迭代器协议的方法:
方法1:传统方式
- 创建一个表示迭代器的新类(例如DeckIterator ) 。
- 在DeckIterator中实现以下两种方法:
__next__
:返回iterable中的下一项。
__iter__
:返回自身,即self
。
3.在类中定义要迭代其实例的__iter__
方法,即Deck类。 该方法应返回DeckIterator的实例。
方法2:务实的方式
- 在Deck类中实现
__iter__
方法作为生成器函数 。
特征
这是实现协议后我们的对象似乎神奇地支持的所有功能的列表。
- 通过for循环进行迭代
- 与元组类似的拆包
- 可以用于列表推导
- 可以与消耗可迭代函数的内置函数(如
min
,max
)一起使用。
>>> new_deck = Deck()
>>> # 1. Looping through a for loop
>>> for card in new_deck:
... print(card) # Works great!
>>> # 2. Unpacking similarly to tuples
>>> first_card, *rest, last_card = new_deck
>>> # 3. List Comprehensions
>>> spades = [card for card in new_deck if card.suit == 'Spades']
>>> # 4. Built-in functions
>>> max_card, min_card = max(new_deck), min(new_deck)
得到教训:
- Python中的迭代器与类型无关,而与协议无关,即,可以迭代实现该协议的任何类。
- Python 反复迭代。
我希望对Iterator协议的了解对您编写Python有所帮助。 为了提高人们对Python似乎不受欢迎的功能的认识,我在PyCon India 2017上提出了关于该主题的演讲 。
From: https://hackernoon.com/how-iterables-actually-work-in-python-65c36ff91c1e