Декоратор Python для обновления экземпляра курсора - PullRequest
2 голосов
/ 08 сентября 2010

У меня есть метод для сохранения данных в БД и декоратор для управления соединением, но я не могу понять, как заставить его работать.

метод сохранения:

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"
                                 )
        )

и у меня здесь унаследованный класс с функцией декоратора, которая не работает.

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
....

Показано это ...

Как мне переопределить DABase.__cursor от декоратора?

Если это невозможно, как решить эту проблему другим способом?

Спасибо, что уделили время!

Ответы [ 2 ]

4 голосов
/ 08 сентября 2010

self - это просто имя, как и все остальное, оно волшебным образом не похоже на Java 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
2 голосов
/ 08 сентября 2010

Было бы полезно, если бы вы показали ошибку, которую вы получаете.Однако, я могу предположить ...

Сложно описать метод класса.Как connectAndDisconnect должен знать, что self должно быть?connectAndDisconnect - это статический метод базового класса, который вызывается при создании производного класса задолго до создания экземпляров производного класса.

Есть хитрость, которая позволяет декоратору выяснить, каким должен быть self, но это сложный взлом и хрупкий способ, который я объясню в конце.Хитрость заключается в том, чтобы использовать класс в качестве декоратора и сделать этот класс дескриптором (т.е. определить __get__), чтобы дать вам возможность определить, каким должен быть self.В вашем случае это выглядело бы примерно так:

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

Производный класс не изменился:

class DA_Row(DABase):
  @DABase.connectAndDisconnect
  def save(self):
    # ...

Теперь DA_Row.save фактически является экземпляром класса connectAndDisconnect.Если d является DA_Row объектом и кто-то вызывает d.save(), первое, что происходит, это то, что connectAndDisconnect.__get__ вызывают, потому что кто-то пытался получить доступ к d.save.Это устанавливает переменную _instance равной d.Затем вызывается connectAndDisconnect.__call__ и происходит фактическое оформление.

В большинстве случаев это работает.Но это хрупко. только работает, если вы вызываете save «обычным» способом, то есть через экземпляр.Если вы попытаетесь сделать такие забавные вещи, как, например, вызов DA_Row.save(d), , он не будет работать , потому что connectAndDisconnect.__get__ не сможет понять, каким должен быть экземпляр.

...