开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜