Python中的“iterable”、“iterator”和“iteration”是什么?它们是如何定义的?


当前回答

其他人已经全面地解释了什么是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是一个具有iter()方法的对象,该方法返回一个迭代器。这是可以循环的。 示例:列表是可迭代的,因为我们可以遍历列表BUT不是迭代器 迭代器是一个可以从中获取迭代器的对象。它是一个具有状态的对象,以便在迭代过程中记住它所处的位置

要查看对象是否有iter()方法,可以使用下面的函数。

ls = ['hello','bye']
print(dir(ls))

输出

['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

正如你所看到的有iter(),这意味着它是一个可迭代对象,但不包含next()方法,这是迭代器对象的一个特征

无论何时在Python中使用for循环或map或列表推导式,都会自动调用next方法以从迭代中获取每一项

迭代对象有一个__iter__方法,每次都会实例化一个新的迭代器。 迭代器实现了一个__next__方法返回单个项,以及一个__iter__方法返回self。 因此,迭代器也是可迭代的,但可迭代的不是迭代器。

卢西亚诺·拉马略,流利的蟒蛇。

下面是我的小抄:

 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:-可迭代的东西是可迭代的;比如序列,比如列表,字符串等等。 它也有__getitem__方法或__iter__方法。现在如果我们对该对象使用iter()函数,我们将得到一个迭代器。

迭代器:-当我们从iter()函数获得迭代器对象;我们调用__next__()方法(在python3中)或简单地调用next()(在python2中)来逐个获取元素。该类或该类的实例称为迭代器。

从文档:

迭代器的使用遍及并统一了Python。在后台,for语句在容器对象上调用iter()。该函数返回一个迭代器对象,该对象定义了__next__()方法,该方法每次访问容器中的一个元素。当没有更多的元素时,__next__()会引发一个StopIteration异常,告诉for循环终止。你可以使用next()内置函数调用__next__()方法;这个例子展示了它是如何工作的:

>>> s = 'abc'
>>> it = iter(s)
>>> it
<iterator object at 0x00A1DB50>
>>> next(it)
'a'
>>> next(it)
'b'
>>> next(it)
'c'
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    next(it)
StopIteration

一个类的前:-

class Reverse:
    """Iterator for looping over a sequence backwards."""
    def __init__(self, data):
        self.data = data
        self.index = len(data)
    def __iter__(self):
        return self
    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]


>>> rev = Reverse('spam')
>>> iter(rev)
<__main__.Reverse object at 0x00A1DB50>
>>> for char in rev:
...     print(char)
...
m
a
p
s