Python decorator to refresh cursor instance
I have a method to save data in DB, and a decorator to manage the connection, but I can't figure out how to make it work.
method to save:
class DA_Row(DABase):
@DABase.connectAndDisconnect
def save(self):
"""
Guarda el spin en la base de datos
"""
self.__cursor.callproc('sp_insert_row', (
"value 1",
"value 2"
)
)
and I have here the inherited class with a function decorator that does not work.
class DABase():
def __init__(self):
self.__cursor = None
@staticmethod
def connectAndDisconnect(func):
def deco(*args):
returnValue = None
self.DBconnect()
try:
self.__cursor = self.db.cursor()
returnValue = func(*args)
finally:
self.desconectarDB()
return returnValue
return deco
....
Shown this...
How can I redefine DABase.__cursor
from a decorator?
If is not possible开发者_运维百科, how to solve this problem in a different way?
Thank you for your time!
self
is just a name like everything else, it does not magically appear like Java's this
. You need to add it to your decorator. Try this:
@staticmethod
def connectAndDisconnect(func):
# deco will be a method, so it needs self (ie a DA_Row instance)
def deco(self, *args):
returnValue = None
self.DBconnect()
try:
self.__cursor = self.db.cursor()
# func was supposed to be a method to, so it needs self
returnValue = func(self, *args)
finally:
self.desconectarDB()
return returnValue
return deco
It would help if you showed the error that you're getting. However, I can take a guess...
Decorating a method of a class is hard. How is connectAndDisconnect
supposed to know what self
should be? connectAndDisconnect
is a static method of the base class, which gets called when the derived class gets created, long before any instances of the derived class get created.
There's a trick which lets the decorator figure out what self
should be, but it's a complicated hack and fragile in a way that I'll explain at the end. The trick is, use a class as the decorator, and make that class an descriptor (i.e. define __get__
) to give you a chance to determine what self
should be. In your case it would look something like:
class DABase(object):
def __init__(self):
self.__cursor = None
class connectAndDisconnect(object):
def __init__(self, method):
self._method = method # method is the thing being decorated
# note that it's an *unbound* method
self._instance = None # no way to know what the instance is yet
def __get__(self, instance, owner):
'This will be called when the decorated method is accessed'
self._instance = instance
return self
def __call__(self, *args):
'This is where the actual decoration takes place'
returnValue = None
# 'self' is the connectAndDisconnect object. 'self._instance' is the decorated object.
self._instance.DBConnect()
try:
self._instance.__cursor = self._instance.db.cursor()
# Since self._method is unbound, we have to pass the instance explicitly
returnValue = self._method(self._instance, *args)
finally:
self._instance.desconectarDB()
return returnValue
The derived class is unchanged:
class DA_Row(DABase):
@DABase.connectAndDisconnect
def save(self):
# ...
Now DA_Row.save
is actually an instance of the connectAndDisconnect
class. If d
is a DA_Row
object and someone calls d.save()
, the first thing that happens is that connectAndDisconnect.__get__
gets called because someone tried to access d.save
. This sets the _instance
variable to equal d
. Then connectAndDisconnect.__call__
gets called and the actual decoration takes place.
This works, most of the time. But it's fragile. It only works if you call save
in the "normal" way, i.e. through an instance. If you try to do funny stuff like calling DA_Row.save(d)
instead, it won't work because connectAndDisconnect.__get__
won't be able to figure out what the instance should be.
精彩评论