打破一个循环,但“完成”发电机

时间:2018-06-08 11:26:30

标签: python

_

考虑以下示例:

def generator(iterable):
    print('Start')
    for item in iterable: yield item
    print('Stop')

for x in generator(range(10)):
    print(x)
    if x==3: break
    print(x)

,其输出为

Start
0
0
1
1
2
2
3

Python当然会完全按照它所说的去做。在x == 3之后不再调用生成器,因此永远不会打印“停止”。生成器抽象是合理的。然而,在这种情况下,我实际上试图实现一种微妙的不同类型的抽象,有点像装饰循环以使其成为自定义循环。有些代码应该在循环之前运行,一些代码用于每次迭代,一些代码应该在循环之后运行,即使在break的情况下也是如此。

当然,我不依赖于这种确切的抽象来使我的程序工作,但它会很好。有没有人对这个案子有任何好的想法?

亲切的问候。

3 个答案:

答案 0 :(得分:4)

生成器是循环的良好抽象,但对于前后代码,Python有另一个抽象 - 上下文管理器和'with'语句。您可以将这两者结合使用,例如:这样:

class generator_context:
    def __init__(self, iterable):
        self.iterable = iterable

    def __enter__(self):
        print('Start')
        for item in self.iterable: yield item

    def __exit__(self, e_type, e_value, e_traceback):
        if e_type is None:
            print('Stop')

with generator_context(range(10)) as generator:
    for x in generator:
        print(x)
        if x==3: break
        print(x)

答案 1 :(得分:3)

好吧,你可以试试看你的发电机>最后。

def generator(iterable):
    try:
        print('Start')
        for item in iterable: yield item
    finally:
        print('Stop')

这使得内部最终总是执行。

答案 2 :(得分:1)

正如我在评论中提到的,上下文管理器可能会做你想要的。这是一个自动关闭生成器的方法(这是有意义的,因为"stop"被打印为表示没有更多的值)。

class my_generator:
    def __init__(self, iterable):
        self.g = (x for x in iterable)

    def __iter__(self):
        return self.g

    def __enter__(self):
        print('start')
        return self

    def __exit__(self, exc_type, exc_value, trace):
        self.g.close() 

        # do whatever cleanup is necessary in case of exception
        print('stop')

用法:

>>> with my_generator([1,2,3]) as g:
...     for x in g:
...         print(x)
...         if x == 2:
...             break
... 
start
1
2
stop
>>> list(g)
[]