4.6 带有外部状�的生�器函数¶

问题¶

你想定义一个生�器函数,但是它会调用�个你想暴露给用户使用的外部状�值。

解决方案¶

如果你想让你的生�器暴露外部状�给用户, 别忘了你�以简�的将它实现为一个类,然�把生�器函数放到 __iter__() 方法中过去。比如:

from collections import deque

class linehistory:
    def __init__(self, lines, histlen=3):
        self.lines = lines
        self.history = deque(maxlen=histlen)

    def __iter__(self):
        for lineno, line in enumerate(self.lines, 1):
            self.history.append((lineno, line))
            yield line

    def clear(self):
        self.history.clear()

为了使用这个类,你�以将它当�是一个普通的生�器函数。 然而,由于�以创建一个实例对象,于是你�以访问内部属性值, 比如 history 属性或者是 clear() 方法。代�示例如下:

with open('somefile.txt') as f:
    lines = linehistory(f)
    for line in lines:
        if 'python' in line:
            for lineno, hline in lines.history:
                print('{}:{}'.format(lineno, hline), end='')

讨论¶

关于生�器,很容易掉进函数无所�能的陷阱。 如果生�器函数需�跟你的程�其他部分打交�的�(比如暴露属性值,�许通过方法调用�控制等等), �能会导致你的代�异常的��。 如果是这�情况的�,�以考虑使用上�介�的定义类的方�。 在 __iter__() 方法中定义你的生�器�会改�你任何的算法逻辑。 由于它是类的一部分,所以�许你定义��属性和方法�供用户使用。

一个需�注�的�地方是,如果你在迭代�作时�使用for循环语�,那么你得先调用 iter() 函数。比如:

>>> f = open('somefile.txt')
>>> lines = linehistory(f)
>>> next(lines)
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
TypeError: 'linehistory' object is not an iterator

>>> # Call iter() first, then start iterating
>>> it = iter(lines)
>>> next(it)
'hello world\n'
>>> next(it)
'this is a test\n'
>>>