【问题标题】:What's the advantage of using yield in __iter__()?在 __iter__() 中使用 yield 有什么好处?
【发布时间】:2018-01-22 22:37:32
【问题描述】:

__iter__() 函数中使用生成器(yield) 有什么好处?通读 Python Cookbook 后,我明白“如果您希望生成器向用户公开额外的状态,请不要忘记您可以轻松 将其实现为一个类,将生成器函数代码放在__iter__() 方法中。”

import io

class playyield:
    def __init__(self,fp):
        self.completefp = fp

    def __iter__(self):
        for line in self.completefp:
            if 'python' in line:
                yield line

if __name__ =='__main__':
    with io.open(r'K:\Data\somefile.txt','r') as fp:
        playyieldobj = playyield(fp)
        for i in playyieldobj:
            print I

问题

  1. 这里的额外状态是什么意思?
  2. __iter__ () 中使用yield 而不是为yield 使用单独的函数有什么好处?

【问题讨论】:

  • 因为现在playyielditerable,但您不必编写iterator 类,因为现在playyied.__iter__ 返回一个生成器,这是一个迭代器。挺方便的。
  • 您的问题没有提供完整的上下文。您在 Python Cookbook 中提到的引用与问题 **Problem:** You would like to define a generator function, but it involves extra state that you would like to expose to the user somehow 有关。这里的extra state暗示了与程序其他部分相关的其他信息。
  • 我也建议检查一下blog post

标签: python yield-keyword


【解决方案1】:

如果您想遵循最佳实践,如果没有生成器函数,则必须实现类似的东西:

In [7]: class IterableContainer:
   ...:     def __init__(self, data=(1,2,3,4,5)):
   ...:         self.data = data
   ...:     def __iter__(self):
   ...:         return IterableContainerIterator(self.data)
   ...:

In [8]: class IterableContainerIterator:
   ...:     def __init__(self, data):
   ...:         self.data = data
   ...:         self._pos = 0
   ...:     def __iter__(self):
   ...:         return self
   ...:     def __next__(self):
   ...:         try:
   ...:              item = self.data[self._pos]
   ...:         except IndexError:
   ...:             raise StopIteration
   ...:         self._pos += 1
   ...:         return item
   ...:

In [9]: container = IterableContainer()

In [10]: for x in container:
    ...:     print(x)
    ...:
1
2
3
4
5

当然,上面的例子是人为的,但希望你明白这一点。使用生成器,这可以很简单:

In [11]: class IterableContainer:
    ...:     def __init__(self, data=(1,2,3,4,5)):
    ...:         self.data = data
    ...:     def __iter__(self):
    ...:         for x in self.data:
    ...:             yield x
    ...:
    ...:

In [12]: list(IterableContainer())
Out[12]: [1, 2, 3, 4, 5]

至于状态,嗯,就是这样 - 对象可以有状态,例如属性。您可以在运行时操纵该状态。您可以执行以下操作,但我认为这是非常不可取的:

In [19]: class IterableContainerIterator:
    ...:     def __init__(self, data):
    ...:         self.data = data
    ...:         self._pos = 0
    ...:     def __iter__(self):
    ...:         return self
    ...:     def __next__(self):
    ...:         try:
    ...:              item = self.data[self._pos]
    ...:         except IndexError:
    ...:             raise StopIteration
    ...:         self._pos += 1
    ...:         return item
    ...:     def rewind(self):
    ...:         self._pos = min(0, self._pos - 1)
    ...:

In [20]: class IterableContainer:
    ...:     def __init__(self, data=(1,2,3,4,5)):
    ...:         self.data = data
    ...:     def __iter__(self):
    ...:         return IterableContainerIterator(self.data)
    ...:

In [21]: container = IterableContainer()

In [22]: it = iter(container)

In [23]: next(it)
Out[23]: 1

In [24]: next(it)
Out[24]: 2

In [25]: it.rewind()

In [26]: next(it)
Out[26]: 1

In [27]: next(it)
Out[27]: 2

In [28]: next(it)
Out[28]: 3

In [29]: next(it)
Out[29]: 4

In [30]: next(it)
Out[30]: 5

In [31]: it.rewind()

In [32]: next(it)
Out[32]: 1

【讨论】:

  • 一年多了,但为什么不在IterableContainer 类中包含__next__?只需将 def __iter__(self): return self__next__ 设置为您希望迭代的类的一部分,现在它不是可迭代的吗?
  • @pstatix 因为这会将它变成一个迭代器。所有 iterators 都是 iterable,但并非所有 iterables 都是 iterators。理想情况下,您应该将两者分开。例如,迭代器应该是single pass,但一般来说,您希望能够多次迭代大多数容器。
  • 我想我在语义上迷失的地方是生成器自动提供__next__,所以虽然没有明确定义为类的一部分,但生成器仍然是迭代器,所以它会推理使用生成器或__iter____next__ 组合使类成为迭代器。
  • 这个answer 说明了我的意思,我们只是在这里讨论关注点分离的最佳实践吗?在我看来,生成器是一个迭代器,因此选择任一方法实际上取决于您希望如何使用该类。但我确实认为,作为生成器,它一次只能迭代一次,而作为迭代器实现允许更多操作。
  • @pstatix 关注点分离是一个优点。 生成器对象 是一个迭代器。生成器函数返回一个生成器对象。因此,由于容器的 __iter__ 返回一个迭代器,它现在是一个可迭代的。但是我们不希望 container 成为 iterator 并实现__next__。我不确定你所说的“更多动作”是什么意思。
猜你喜欢
  • 2019-10-21
  • 2012-01-21
  • 2011-01-15
  • 2016-01-30
  • 2019-01-16
  • 2016-06-27
  • 2016-11-05
  • 2015-10-18
  • 2015-11-05
相关资源
最近更新 更多