了解 class 变量行为

Understanding class variable behavior

我们在 python 2.

的以下代码中发现需要有一个动态 class 变量
from datetime import datetime
from retrying import retry

class TestClass(object):
    SOME_VARIABLE = None

    def __init__(self, some_arg=None):
        self.some_arg = some_arg

    @retry(retry_on_exception=lambda e: isinstance(e, EnvironmentError), wait_fixed=3000 if SOME_VARIABLE == "NEEDED" else  1000, stop_max_attempt_number=3)
    def some_func(self):
        print("Running {} at {}".format(self.some_arg, datetime.now()))
        if self.some_arg != "something needed":
            raise EnvironmentError("Unexpected value")


TestClass.SOME_VARIABLE = "NEEDED"
x = TestClass()
x.some_func()

输出:

Running None at 2021-07-26 19:40:22.374736
Running None at 2021-07-26 19:40:23.376027
Running None at 2021-07-26 19:40:24.377523
Traceback (most recent call last):
  File "/home/raj/tmp/test_test.py", line 19, in <module>
    x.some_func()
  File "/home/raj/.local/share/virtualenvs/test-DzpjW1fZ/lib/python2.7/site-packages/retrying.py", line 49, in wrapped_f
    return Retrying(*dargs, **dkw).call(f, *args, **kw)
  File "/home/raj/.local/share/virtualenvs/test-DzpjW1fZ/lib/python2.7/site-packages/retrying.py", line 212, in call
    raise attempt.get()
  File "/home/raj/.local/share/virtualenvs/test-DzpjW1fZ/lib/python2.7/site-packages/retrying.py", line 247, in get
    six.reraise(self.value[0], self.value[1], self.value[2])
  File "/home/raj/.local/share/virtualenvs/test-DzpjW1fZ/lib/python2.7/site-packages/retrying.py", line 200, in call
    attempt = Attempt(fn(*args, **kwargs), attempt_number, False)
  File "/home/raj/tmp/test_test.py", line 14, in some_func
    raise EnvironmentError("Unexpected value")
EnvironmentError: Unexpected value

我们可以看到 SOME_VARIABLE 的值没有被更新。

试图了解我们是否可以动态更新 SOME_VARIABLE。用例是在运行时根据 SOME_VARIABLE 值在重试函数中进行动态计时。

你的 class 定义等价于

class TestClass(object):
    SOME_VARIABLE = None

    def __init__(self, some_arg=None):
        self.some_arg = some_arg

    decorator = retry(retry_on_exception=lambda e: isinstance(e, EnvironmentError),
                      wait_fixed=3000 if SOME_VARIABLE == "NEEDED" else  1000,
                      stop_max_attempt_number=3)

    def some_func(self):
        ...

    some_func = decorator(some_func)

请注意,在您更改 TestClass.SOME_VARIABLE 的值之前,retry 被称为 long(实际上,在 class 对象之前绑定到 TestClass 甚至存在),因此当 SOME_VARIABLE 仍然等于 None.

时对比较 SOME_VARIABLE == "NEEDED" 求值

要在 运行 时配置重试行为,请尝试

class TestClass(object):
    SOME_VARIABLE = None

    def __init__(self, some_arg=None):
        self.some_arg = some_arg

    def _some_func_implemenation(self):
        print("Running {} at {}".format(self.some_arg, datetime.now()))
        if self.some_arg != "something needed":
            raise EnvironmentError("Unexpected value")

    def some_func(self):
        wait = 3000 if self.SOME_VARIABLE == "NEEDED" else 1000
        impl = retry(retry_on_exception=lambda e: isinstance(e, EnvironmentError), 
                     wait_fixed=wait,
                     stop_max_attempt_number=3)(self._some_func)
        return impl()

some_func 成为一个函数,在 运行 时间创建一个具有适当重试行为的函数(基于私有 _some_func),然后调用它。

(未测试;我可能将绑定方法 self._some_funcretry 之间的交互弄错了。)