Is there an equivalent of __enter__ and __exit__ for all functions in the class?

One python functionality that I found very convenient when working with databases (or files) is the __enter__ and __exit__ functions that you can give to a class. Now with the help of the with statement you can make sure that __enter__ first called in this block (and you can open the database or file), and after its execution __exit__ is called (and you can close the database or file.

I want to open and close a sqlite transaction every time a function is called from my database class. I can do this at the beginning and at the end of each function, but since it needs to be done for each function, this is a class, I was wondering if there are methods called before and after each function call? Like SetUp and TearDown in unittesting.

+4
source share
1 answer

You can decorate each function with a cake decorator, something like

 @transaction def insertData(self): # code 

and the transaction is a decorator that you define to wrap the function with pre and post. Yes, you have to do this for every function. Here is an example

 def transaction(f): def pre(): print "pre transaction" def post(): print "post transaction" def wrapped(*args): pre() f(*args) post() return wrapped class Foo(object): def __init__(self): print "instantiating" def doFoo(self): print "doing foo" @transaction def doBar(self, value): print "doing bar "+str(value) @transaction def foofunc(): print "hello" foofunc() f=Foo() f.doFoo() f.doBar(5) 

.

 stefanos-imac:python borini$ python decorator.py pre transaction hello post transaction instantiating doing foo pre transaction doing bar 5 post transaction 

An alternative is to use a metaclass, for example:

 import types class DecoratedMetaClass(type): def __new__(meta, classname, bases, classDict): def pre(): print "pre transaction" def post(): print "post transaction" newClassDict={} for attributeName, attribute in classDict.items(): if type(attribute) == types.FunctionType: def wrapFunc(f): def wrapper(*args): pre() f(*args) post() return wrapper newAttribute = wrapFunc(attribute) else: newAttribute = attribute newClassDict[attributeName] = newAttribute return type.__new__(meta, classname, bases, newClassDict) class MyClass(object): __metaclass__ = DecoratedMetaClass def __init__(self): print "init" def doBar(self, value): print "doing bar "+str(value) def doFoo(self): print "doing foo" c = MyClass() c.doFoo() c.doBar(4) 

This is pure black magic, but it works.

 stefanos-imac:python borini$ python metaclass.py pre transaction init post transaction pre transaction doing foo post transaction pre transaction doing bar 4 post transaction 

Usually you do not want to decorate __init__ , and you can only decorate these methods with a special name, so you might want to replace

  for attributeName, attribute in classDict.items(): if type(attribute) == types.FunctionType: 

with something like

  for attributeName, attribute in classDict.items(): if type(attribute) == types.FunctionType and "trans_" in attributeName[0:6]: 

Thus, only methods called trans_whatever will be processed.

+7
source

Source: https://habr.com/ru/post/1411543/


All Articles