Python 装饰器刷新游标实例

2 投票
2 回答
1702 浏览
提问于 2025-04-16 03:51

我有一个方法用来把数据保存到数据库里,还有一个装饰器用来管理连接,但我不知道怎么让它们一起工作。

保存数据的方法是:

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 个回答

2

如果你能把遇到的错误信息给我看就更好了。不过,我可以猜一下...

给一个类的方法加装饰器是比较复杂的。像 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__ 无法确定实例应该是什么。

4

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

撰写回答