Non c'è un modo semplice, per quanto ne so. Ma puoi avere un flag booleano, per verificare se è stato invocato __enter__
, prima che venissero chiamati i metodi effettivi negli oggetti.
class MyContextManager(object):
def __init__(self):
self.__is_context_manager = False
def __enter__(self):
print "Entered"
self.__is_context_manager = True
return self
def __exit__(self, exc_type, exc_value, traceback):
print "Exited"
def do_something(self):
if not self.__is_context_manager:
raise Exception("MyContextManager should be used only with `with`")
print "I don't know what I am doing"
Quando lo si utilizza con with
,
with MyContextManager() as y:
y.do_something()
otterrete
Entered
I don't know what I am doing
Exited
Ma, quando si crea manualmente un oggetto, e richiama do_something
,
x = MyContextManager()
x.do_something()
otterrete
Traceback (most recent call last):
File "/home/thefourtheye/Desktop/Test.py", line 22, in <module>
x.do_something()
File "/home/thefourtheye/Desktop/Test.py", line 16, in do_something
raise Exception("MyContextManager should be used only with `with`")
Exception: MyContextManager should be used only with `with`
Nota: Questa non è una soluzione solida. Qualcuno può invocare direttamente il metodo __enter__
prima di chiamare qualsiasi altro metodo e il metodo __exit__
non può mai essere chiamato in quel caso.
Se non si vuole ripetere che il check in ogni funzione, si può rendere un decoratore, come questo
class MyContextManager(object):
def __init__(self):
self.__is_context_manager = False
def __enter__(self):
print "Entered"
self.__is_context_manager = True
return self
def __exit__(self, exc_type, exc_value, traceback):
print "Exited"
def ensure_context_manager(func):
def inner_function(self, *args, **kwargs):
if not self.__is_context_manager:
raise Exception("This object should be used only with `with`")
return func(self, *args, **kwargs)
return inner_function
@ensure_context_manager
def do_something(self):
print "I don't know what I am doing"
E ** perché in nome del cielo ** vorresti mai farlo? 'x = X()', 'con x come result_of_entering:' (creando il CM e usandolo su due linee separate) improvvisamente non è più un caso d'uso valido? Cosa succede se volevo memorizzare i gestori di contesto in una mappatura per selezionarne uno in modo dinamico? –
E se volessi usare un ['contextlib.ExitStack()'] (https://docs.python.org/3/library/contextlib.html#contextlib.ExitStack)? combinare più gestori di contesto? Esistono diversi buoni casi di utilizzo in cui viene creato un gestore di contesto al di fuori di un'istruzione 'with' che si sta tentando di impedire. Non cercare di correggere tutti i possibili errori del programmatore a scapito di rendere la vita più difficile per quei programmatori che sanno cosa stanno facendo. –