Writing a Python class that can only be used as a context manager

Is there a way in Python to write a class that will be erroneous if it is not used with the with statement?

# Okay: with Foo() as f1: f1.func1() f1.func2() # Not okay: f2 = Foo() f2.func1() 

I can do this manually: let __enter__ set a flag and check every other method for that flag. But is there a better way to do this?

Here's the code for the wrong way:

 class Foo(object): def __init__(self): self._entered = False def __enter__(self): self._entered = True return self def _verify_entered(self): if not self._entered: raise Exception("Didn't get call to __enter__") def __exit__(self, typ, val, traceback): self._verify_entered() print("In __exit__") def func1(self): self._verify_entered() # do stuff ... def func2(self): self._verify_entered() # do other stuff 
+7
python contextmanager
source share
2 answers

Technically, I think agf has this right in the sense that you can use the metaclass to automate material. However, if I understand the main motivation correctly, I would suggest a different way.

Suppose you have a Payload class that you want to protect with the context manager. In this case, you simply create a context manager that returns it:

 # This should go in a private module. class Payload(object): def __init__(self): print 'payload ctor' # This should go in the public interface. class Context(object): def __init__(self): # Set up here the parameters. pass def __enter__(self): # Build & return a Payload object return Payload() def __exit__(self, exc_type, exc_val, exc_tb): # Cleanup pass with Context() as f: # f here is a Payload object. 

If you hide Payload in a private module, you are good.

+3
source share

You can return the __enter__ method __enter__ different object than self if you do not want the user to be able to call methods in the context manager object itself.

 class Foo(object): def __enter__(self): print("In __enter__") return Bar() def __exit__(self, typ, val, traceback): print("In __exit__") class Bar(object): def func1(self): print("In func1") def func2(self): print("In func2") 

Of course, you can have the Foo and Bar classes more related to each other than in this example. For example, the Foo class may pass itself to the Bar constructor in __enter__ .

Calling Foo().func1() will not work for the obvious reason that Foo does not have such a method. If you want Bar not to be visible to the user, you can prefix his name with an underscore (hinting that it is internal) or even nest it in the Foo class (or even the __enter__ method if you really want to be extreme).

+2
source share

All Articles