Python中的“iterable”、“iterator”和“iteration”是什么?它们是如何定义的?
当前回答
这是另一个使用collections.abc的视图。这个视图在第二次或以后可能会有用。
从集合。ABC我们可以看到下面的层次结构:
builtins.object
Iterable
Iterator
Generator
例如,Generator是由Iterator派生的Iterable是由基对象派生的。
因此,
Every iterator is an iterable, but not every iterable is an iterator. For example, [1, 2, 3] and range(10) are iterables, but not iterators. x = iter([1, 2, 3]) is an iterator and an iterable. A similar relationship exists between Iterator and Generator. Calling iter() on an iterator or a generator returns itself. Thus, if it is an iterator, then iter(it) is it is True. Under the hood, a list comprehension like [2 * x for x in nums] or a for loop like for x in nums:, acts as though iter() is called on the iterable (nums) and then iterates over nums using that iterator. Hence, all of the following are functionally equivalent (with, say, nums=[1, 2, 3]): for x in nums: for x in iter(nums): for x in iter(iter(nums)): for x in iter(iter(iter(iter(iter(nums))))):
其他回答
下面是我的小抄:
sequence
+
|
v
def __getitem__(self, index: int):
+ ...
| raise IndexError
|
|
| def __iter__(self):
| + ...
| | return <iterator>
| |
| |
+--> or <-----+ def __next__(self):
+ | + ...
| | | raise StopIteration
v | |
iterable | |
+ | |
| | v
| +----> and +-------> iterator
| ^
v |
iter(<iterable>) +----------------------+
|
def generator(): |
+ yield 1 |
| generator_expression +-+
| |
+-> generator() +-> generator_iterator +-+
小测验:你看到…
每个迭代器都是可迭代对象? 容器对象的__iter__()方法可以实现为生成器? 具有__next__方法的迭代器不一定是迭代器?
答案:
Every iterator must have an __iter__ method. Having __iter__ is enough to be an iterable. Therefore every iterator is an iterable. When __iter__ is called it should return an iterator (return <iterator> in the diagram above). Calling a generator returns a generator iterator which is a type of iterator. class Iterable1: def __iter__(self): # a method (which is a function defined inside a class body) # calling iter() converts iterable (tuple) to iterator return iter((1,2,3)) class Iterable2: def __iter__(self): # a generator for i in (1, 2, 3): yield i class Iterable3: def __iter__(self): # with PEP 380 syntax yield from (1, 2, 3) # passes assert list(Iterable1()) == list(Iterable2()) == list(Iterable3()) == [1, 2, 3] Here is an example: class MyIterable: def __init__(self): self.n = 0 def __getitem__(self, index: int): return (1, 2, 3)[index] def __next__(self): n = self.n = self.n + 1 if n > 3: raise StopIteration return n # if you can iter it without raising a TypeError, then it's an iterable. iter(MyIterable()) # but obviously `MyIterable()` is not an iterator since it does not have # an `__iter__` method. from collections.abc import Iterator assert isinstance(MyIterable(), Iterator) # AssertionError
在处理可迭代对象和迭代器之前,决定可迭代对象和迭代器的主要因素是序列
序列:序列是数据的集合
Iterable: Iterable是支持__iter__方法的序列类型对象。
Iter方法:Iter方法以序列为输入,创建一个对象,称为迭代器
迭代器:迭代器是调用next方法和横向遍历序列的对象。在调用下一个方法时,它返回当前遍历的对象。
例子:
x=[1,2,3,4]
X是一个由数据集合组成的序列
y=iter(x)
在调用iter(x)时,只有当x对象有iter方法时,它才会返回一个迭代器,否则会引发异常。如果返回迭代器,则y的赋值如下:
y=[1,2,3,4]
因为y是一个迭代器,所以它支持next()方法
在调用next方法时,它逐个返回列表中的各个元素。
返回序列的最后一个元素后,如果再次调用下一个方法,将引发StopIteration错误
例子:
>>> y.next()
1
>>> y.next()
2
>>> y.next()
3
>>> y.next()
4
>>> y.next()
StopIteration
其他人已经全面地解释了什么是iterable和iterator,所以我将尝试对生成器做同样的事情。
恕我直言,理解生成器的主要问题是“生成器”这个词的混淆用法,因为这个词有两种不同的含义:
作为创建(生成)迭代器的工具, 以返回迭代器的函数形式(即在函数体中包含yield语句), 以生成器表达式的形式 作为使用该工具的结果,即结果迭代器。 (在这个意思中,生成器是迭代器的一种特殊形式——“generator”这个词指出了这个迭代器是如何创建的。)
Generator作为第一种工具:
In[2]: def my_generator():
...: yield 100
...: yield 200
In[3]: my_generator
输出[3]:<function __main__.my_generator()> .my_generator(
In[4]: type(my_generator)
[4]:函数
生成器作为使用此工具的结果(即迭代器):
In[5]: my_iterator = my_generator()
In[6]: my_iterator
输出[6]:<生成器对象my_generator at 0x00000000053EAE48>
In[7]: type(my_iterator)
[7]:发电机
Generator作为第二种类型的工具-与该工具的结果迭代器难以区分:
In[8]: my_gen_expression = (2 * i for i in (10, 20))
In[9]: my_gen_expression
Out[9]: <generator object <genexpr> at 0x000000000542C048>
In[10]: type(my_gen_expression)
[10]:发电机
迭代器是实现iter和next方法的对象。如果定义了这些方法,则可以使用for循环或推导式。
class Squares:
def __init__(self, length):
self.length = length
self.i = 0
def __iter__(self):
print('calling __iter__') # this will be called first and only once
return self
def __next__(self):
print('calling __next__') # this will be called for each iteration
if self.i >= self.length:
raise StopIteration
else:
result = self.i ** 2
self.i += 1
return result
迭代器会耗尽。这意味着在你遍历项目之后,你不能重复,你必须创建一个新对象。假设你有一个类,它包含cities属性,你想要遍历。
class Cities:
def __init__(self):
self._cities = ['Brooklyn', 'Manhattan', 'Prag', 'Madrid', 'London']
self._index = 0
def __iter__(self):
return self
def __next__(self):
if self._index >= len(self._cities):
raise StopIteration
else:
item = self._cities[self._index]
self._index += 1
return item
类Cities的实例是一个迭代器。然而,如果你想在城市上重复,你必须创建一个新对象,这是一个昂贵的操作。你可以把这个类分成两个类:一个返回城市,第二个返回一个迭代器,它将城市作为初始参数。
class Cities:
def __init__(self):
self._cities = ['New York', 'Newark', 'Istanbul', 'London']
def __len__(self):
return len(self._cities)
class CityIterator:
def __init__(self, city_obj):
# cities is an instance of Cities
self._city_obj = city_obj
self._index = 0
def __iter__(self):
return self
def __next__(self):
if self._index >= len(self._city_obj):
raise StopIteration
else:
item = self._city_obj._cities[self._index]
self._index += 1
return item
现在如果我们需要创建一个新的迭代器,我们不需要再次创建数据,也就是城市。我们创建了cities对象并将其传递给迭代器。但我们仍在做额外的工作。我们可以通过只创建一个类来实现这一点。
Iterable是一个实现Iterable协议的Python对象。它只需要返回一个迭代器对象的新实例的__iter__()。
class Cities:
def __init__(self):
self._cities = ['New York', 'Newark', 'Istanbul', 'Paris']
def __len__(self):
return len(self._cities)
def __iter__(self):
return self.CityIterator(self)
class CityIterator:
def __init__(self, city_obj):
self._city_obj = city_obj
self._index = 0
def __iter__(self):
return self
def __next__(self):
if self._index >= len(self._city_obj):
raise StopIteration
else:
item = self._city_obj._cities[self._index]
self._index += 1
return item
迭代器有__iter__和__next__,可迭代对象有__iter__,所以我们可以说迭代器也是可迭代对象,但它们是耗尽的可迭代对象。另一方面,迭代对象永远不会耗尽 因为它们总是返回一个新的迭代器,然后用于迭代
你注意到可迭代器代码的主要部分是在迭代器中,而可迭代器本身只不过是一个额外的层,允许我们创建和访问迭代器。
在可迭代对象上迭代
Python有一个内置的函数iter(),它调用__iter__()。当我们遍历一个可迭代对象时,Python调用iter(),它返回一个迭代器,然后它开始使用迭代器的__next__()来遍历数据。
注意,在上面的例子中,Cities创建了一个可迭代对象,但它不是一个序列类型,这意味着我们不能通过索引获得一个城市。为了解决这个问题,我们应该将__get_item__添加到Cities类中。
class Cities:
def __init__(self):
self._cities = ['New York', 'Newark', 'Budapest', 'Newcastle']
def __len__(self):
return len(self._cities)
def __getitem__(self, s): # now a sequence type
return self._cities[s]
def __iter__(self):
return self.CityIterator(self)
class CityIterator:
def __init__(self, city_obj):
self._city_obj = city_obj
self._index = 0
def __iter__(self):
return self
def __next__(self):
if self._index >= len(self._city_obj):
raise StopIteration
else:
item = self._city_obj._cities[self._index]
self._index += 1
return item
iterable = [1, 2]
iterator = iter(iterable)
print(iterator.__next__())
print(iterator.__next__())
so,
Iterable是一个可以循环的对象。例如,列表,字符串,元组等。 在iterable对象上使用iter函数将返回一个迭代器对象。 现在这个迭代器对象有一个名为__next__的方法(在Python 3中,或者在Python 2中只是next),通过它你可以访问iterable的每个元素。
所以, 以上代码的输出为:
1
2
推荐文章
- 有没有办法在python中做HTTP PUT
- “foo Is None”和“foo == None”之间有什么区别吗?
- 类没有对象成员
- Django模型“没有显式声明app_label”
- 熊猫能自动从CSV文件中读取日期吗?
- 在python中zip的逆函数是什么?
- 有效的方法应用多个过滤器的熊猫数据框架或系列
- 如何检索插入id后插入行在SQLite使用Python?
- 我如何在Django中添加一个CharField占位符?
- 如何在Python中获取当前执行文件的路径?
- 我如何得到“id”后插入到MySQL数据库与Python?
- super()失败,错误:TypeError "参数1必须是类型,而不是classobj"当父不继承对象
- Python内存泄漏
- 实现嵌套字典的最佳方法是什么?
- 如何在tensorflow中获得当前可用的gpu ?