pass argument to __enter__

Yes, you can get the effect by adding a little more code.


    #!/usr/bin/env python

    class Clippy_Runner( dict ):
        def __init__( self ):
            pass
        def __call__( self, **kwargs ):
            self.update( kwargs )
            return self
        def __enter__( self ):
            return self
        def __exit__( self, exc_type, exc_val, exc_tb ):
            self.clear()

    clippy_runner = Clippy_Runner()

    print clippy_runner.get('verbose')     # Outputs None
    with clippy_runner(verbose=True):
        print clippy_runner.get('verbose') # Outputs True
    print clippy_runner.get('verbose')     # Outputs None

No. You can't. You pass arguments to __init__().

class ClippyRunner:
    def __init__(self, *args):
       self._args = args

    def __enter__(self):
       # Do something with args
       print(self._args)


with ClippyRunner(args) as something:
    # work with "something"
    pass

The accepted answer (Which I feel is incorrect) states that you CAN'T, and that you should instead do;

class Comedian:
    def __init__(self, *jokes):
        self.jokes = jokes
    def __enter__(self):
        jokes = self.jokes
        #say some funny jokes
        return self

..and while this is often what you would do, it is not always the best solution, or even a solution, and it is definitely not the only solution!..

I assume that what you want is to be able to do something similar to;

funny_object = Comedian()
with funny_object('this is a joke') as humor:
    humor.say_something_funny()

If this is the case, and it is not more complicated than that, then you can just do;

class Comedian:
    def __enter__(self):
        jokes = self.jokes
        #say some funny jokes
        return self
    def __call__(self, *jokes):
        self.jokes = jokes
        return self  # EDIT as pointed out by @MarkLoyman

..That way you can still initialize the object with any arguments that you want, and do any other stuff with the object like you would usually, but when you go to use the object as a context manager you first call its call function and set up some args for the context manager.

The important thing here is to understand exactly how context managers work in Python.

In Python a context manager is any object that defines an enter method. This method is called automatically when you do;

with object as alias:
    alias.do_stuff()
    ..

..Notice how object doesn't have a couple of "()" after it, it is an implicit function call, and it doesn't take any arguments.

You might have gotten the idea of passing arguments to enter from;

with open(filename) as file:
    "do stuff with file..

But this is different from overriding enter, as "open" is not an object, but a function.

A good exercise is to open an interactive python console and type "open" + [ENTER]

>>> open
<built-in function open>

"open" is not a context manager object, but function. It doesn't have an enter method at all, instead it is defined in the following way;

@contextmanager
def open(..):
    ...

..you can define your own context manager functions in the same way, you can even override the definition of "open".

IMO though, the best thing to do if you need to create an object and then later use it as a context manager with arguments (..what I do) is to give the object a method that returns a temporary object that defines an enter method, like so;

class Comedian:
    def context(audience):
        class Roaster:
            context = audience
            def __enter__(self):
                audience = self.__class__.context
                # a comedian needs to know his/her audience.
        return Roaster(audience)

funny_thing = Comedian()
with funny_thing.context('young people') as roaster:
    roaster.roast('old people')

The order of the call-chain in this example is; Comedian.__init__() -> Comedian.context(args) -> Roaster.__enter__()

I felt like this answer was missing from the lot, so I added it.

EDIT: Added "return self" to Comedian.__call__ as pointed out by @MarkLoyman


You can use the contextmanager decorator to pass arguments:

https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager

from contextlib import contextmanager

@contextmanager
def clippy_runner(*args):
    yield

IMHO, I find confusing that using contextmanager you can provide arguments, but you cannot provide them to __enter__