Search code examples
pythonpython-3.xgeneratorcontextmanager

Generator and context manager at the same time


Imagine I have some code that I want it to run:

with F() as o:
    while True:
        a = o.send(2)
        print(a)

It means that the F class should return an generator and also it is context manager, generally I want a context manager to be generator too.

I tried this:

class F:

    def __enter__(self):
        return self

    def __exit__(self, *exc):
        print('exit')

    def __next__(self):
        return 5

    def __iter__(self):
        return self

As expected this will return AttributeError: 'F' object has no attribute 'send', I handled this error by adding:

def send(self, param):
    self.__next__()

but I think it is not a good way to do this, I look around and find this, but they are not using send as I want, I need that instance to be a generator.


Solution

  • You can use collections.abc and subclass your class F from Generator (manual pages). If you implement enter and exit, your instance will be generator and have context manager support as well:

    from collections.abc import Generator
    
    class F(Generator):
        def __init__(self):
            self.__my_generator = self._my_generator()
            next(self.__my_generator)   # prime the generator
    
        def _my_generator(self):
            while True:
                v = yield 42
                print('generator received ', v)
    
        # context manager interace:
        def __enter__(self):
            return self
    
        def __exit__(self, *exc):
            print('exit')
    
        # Generator interface:
        def send(self, value):
            return self.__my_generator.send(value)
    
        def throw(self, typ, value=None, traceback=None):
            return self.__my_generator.throw(typ, value, traceback)
    
    
    with F() as o:
        while True:
            a = o.send(2)
            print('I received ', a)
    

    Prints:

    generator received  2
    I received  42
    ...etc.