【问题标题】:Referencing self in decorator [duplicate]在装饰器中引用 self [重复]
【发布时间】:2020-04-02 11:39:18
【问题描述】:

我正在 python 中实现一个数据库连接器类。我将使用tenacity库中的retry装饰器在超时时重试数据库连接。

我想将self.retry_countself.retry_interval 传递给retry 装饰器中的参数。

## etl_connect.py
from sqlalchemy import create_engine
import pymysql
import logging
from tenacity import *

class Connector():
    def __init__(self, mode, conn_str, retry_count, retry_interval):
        self.mode = mode
        self.conn_str = conn_str
        self.retry_count = retry_count
        self.retry_interval = retry_interval
        self.engine = None
        self.conn = None

    @retry(wait=wait_fixed(self.retry_interval), stop=stop_after_attempt(self.retry_count))
    def mysql_connect(self):
        logging.info('Connecting to mysql. (retry count=%d)' % (self.mysql_connect.retry.statistics['attempt_number']))
        mysql_engine = create_engine(self.conn_str)
        mysql_conn = mysql_engine.connect()
        logging.info('Connected to mysql successfully with %d attempt(s).' % (self.mysql_connect.retry.statistics['attempt_number']))
        return (mysql_engine, mysql_conn)

现在调用mysql_connect函数:

## call.py
from etl_connect import *
mysql_connector = Connector('mysql', 'mysql database string here', 5, 10)
engine, conn = mysql_connector.mysql_connect()

但它显示:NameError: name 'self' is not defined

Traceback (most recent call last):
  File "call.py", line 5, in <module>
    from etl_connect import *
  File "/home/developer/ETL_modules/etl_connect.py", line 19, in <module>
    class Connector():
  File "/home/developer/ETL_modules/etl_connect.py", line 56, in Connector
    @retry(wait=wait_fixed(self.retry_interval), stop=stop_after_attempt(self.retry_count))
NameError: name 'self' is not defined

有什么方法可以将self.retry_countself.retry_interval 传递给装饰器?

【问题讨论】:

标签: python python-3.x python-decorators python-tenacity


【解决方案1】:

当你调用方法时调用retry而不是装饰方法。

## etl_connect.py
from sqlalchemy import create_engine
import pymysql
import logging
from tenacity import *

class Connector():
    def __init__(self, mode, conn_str, retry_count, retry_interval):
        self.mode = mode
        self.conn_str = conn_str
        self.retry_count = retry_count
        self.retry_interval = retry_interval
        self.engine = None
        self.conn = None

    def _mysql_connect(self):
        logging.info('Connecting to mysql. (retry count=%d)' % (self.mysql_connect.retry.statistics['attempt_number']))
        mysql_engine = create_engine(self.conn_str)
        mysql_conn = mysql_engine.connect()
        logging.info('Connected to mysql successfully with %d attempt(s).' % (self.mysql_connect.retry.statistics['attempt_number']))
        return (mysql_engine, mysql_conn)

    def mysql_connect(self):
        d = retry(
              wait=wait_fixed(self.retry_interval),
              stop=stop_after_attempt(self.retry_count)
            )
        # One of these two should work, depending on how
        # retry is actually defined.
        return d(Connector._mysql_connect)(self)
        # return d(self._mysql_connect)

【讨论】:

  • 这对我有用。你能解释一下为什么“(自我)”是必要的吗?
  • 你在说哪个self
  • 抱歉没有澄清——mysql_connect的return语句中的(self)
  • 在任何一种情况下,_mysql_connect 都是一个实例方法,因此需要使用或在 Connector 的实例上调用。是显式传递它,还是将绑定方法传递给 retry,取决于 retry(在原始问题中未定义)是否采用零参数或 1 参数可调用对象。
【解决方案2】:

除非装饰器(重试)被定义为类的一部分(并且不是静态的),否则您不能引用其中的对象实例。

【讨论】:

    【解决方案3】:

    如果您不访问 @retry 装饰器并且阻止对其进行编辑,则可以在类定义之外定义一个变量。或者,您可以在设置文件中定义此基本配置,然后将其导入。请看这个:

    ## etl_connect.py
    from sqlalchemy import create_engine
    import pymysql
    import logging
    from tenacity import *
    
    
    base_config = {
        'retry_count': 3,
        'retry_interval': 30,
        ...
    }
    
    class Connector():
        def __init__(self, mode, conn_str):
            self.mode = mode
            self.conn_str = conn_str
            self.engine = None
            self.conn = None
    
        @retry(wait=wait_fixed(base_config['retry_interval']), stop=stop_after_attempt(base_config['retry_count']))
        def mysql_connect(self):
            logging.info('Connecting to mysql. (retry count=%d)' % (self.mysql_connect.retry.statistics['attempt_number']))
            mysql_engine = create_engine(self.conn_str)
            mysql_conn = mysql_engine.connect()
            logging.info('Connected to mysql successfully with %d attempt(s).' % (self.mysql_connect.retry.statistics['attempt_number']))
            return (mysql_engine, mysql_conn)
    

    或者从设置文件中导入:

    from sqlalchemy import create_engine
    import pymysql
    import logging
    from tenacity import *
    
    from settings import RETRY_COUNT
    from settings import RETRY_INTERVAL
    

    我知道这种方式很可靠,但是这些选项应该在您的设置中定义,并且每次您想要创建类的实例时都不需要传递它们。

    【讨论】:

      【解决方案4】:

      假设您无法轻松地重新定义坚韧 retry 装饰器,您可以使用自己的一个引用 Connector 实例中的值的装饰器来包装它。

      这就是我的意思:

      # Wrapper for tenacity retry decorator
      def my_retry(func):
          def wrapped(conn, *args, **kwargs):
              tdecorator = retry(wait=wait_fixed(conn.retry_interval),
                                 stop=stop_after_attempt(conn.retry_count))
              decorated = tdecorator(func)
              return decorated(conn, *args, **kwargs)
          return wrapped
      
      
      class Connector():
          def __init__(self, mode, conn_str, retry_count, retry_interval):
              self.mode = mode
              self.conn_str = conn_str
              self.retry_count = retry_count
              self.retry_interval = retry_interval
              self.engine = None
              self.conn = None
      
          @my_retry
          def mysql_connect(self):
              logging.info('Connecting to mysql. (retry count=%d)' % (self.mysql_connect.retry.statistics['attempt_number']))
              mysql_engine = create_engine(self.conn_str)
              mysql_conn = mysql_engine.connect()
              logging.info('Connected to mysql successfully with %d attempt(s).' % (self.mysql_connect.retry.statistics['attempt_number']))
              return (mysql_engine, mysql_conn)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-09-01
        • 2012-04-28
        • 2011-10-04
        • 2011-11-20
        • 2012-02-09
        • 2018-06-08
        • 2012-03-20
        相关资源
        最近更新 更多