问题
Is there a way in Python to write a class that will error unless it's used with a with statement?
# Okay:
with Foo() as f1:
f1.func1()
f1.func2()
# Not okay:
f2 = Foo()
f2.func1()
I can do it manually: have __enter__ set a flag and have every other method check for that flag. But is there a nicer way to do it?
Here's code for the not-so-since 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
回答1:
Technically, I think that agf has it right in the sense that you could use a metaclass to automate the stuff. However, if I understand the fundamental motivation behind it correctly, I'd suggest a different way.
Suppose you have a Payload class you want to protect via a 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're good.
回答2:
You can have your __enter__ method return a different object than self if you don't want a user to be able to call methods on 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")
You can of course have the Foo and Bar classes more tied together than I have for this example. For instance, the Foo class could pass itself to the Bar constructor in __enter__.
Calling Foo().func1() will not work for the obvious reason that Foo doesn't have any such method. If you wanted Bar not to be visible to the user, you could prefix its name with an underscore (hinting that it's internal) or even nest it within the Foo class (or even the __enter__ method, if you really want to be extreme).
来源:https://stackoverflow.com/questions/30673402/writing-a-python-class-that-can-only-be-used-as-a-context-manager