装饰的可调用类

时间:2018-04-25 19:59:04

标签: python decorator python-decorators exponential-backoff

我有一个可调用的类:

class CallMeMaybe:

    __name__ = 'maybe'

    def __init__(self):
        self.n_calls = 0

    def __call__(self):
        self.n_calls += 1
        raise Exception

这似乎与宣传的一样有效:

>>> f = CallMeMaybe()
>>> f.n_calls
0
>>> for i in range(7):
...     try:
...         f()
...     except Exception:
...         pass
...     
>>> f.n_calls
7

我想用指数backoff

来装饰它
from backoff import on_exception, expo
dec = on_exception(expo, Exception, max_tries=3, on_backoff=print)
f = CallMeMaybe()
f2 = dec(f)

现在看起来属性访问停止了工作:

>>> f2.n_calls
0
>>> f2()
{'target': <__main__.CallMeMaybe object at 0xcafef00d>, 'args': (), 'kwargs': {}, 'tries': 1, 'elapsed': 2.1e-05, 'wait': 0.4843249208229148}
{'target': <__main__.CallMeMaybe object at 0xcafef00d>, 'args': (), 'kwargs': {}, 'tries': 2, 'elapsed': 0.484935, 'wait': 1.6524016553598126}
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
... blah blah blah
>>> f2.n_calls
0

我的问题:谁将n_calls名称复制到f2的名称空间中,为什么?现在它有一个陈旧的值 - 正确的值应该是3:

>>> f2.__wrapped__.n_calls
3

1 个答案:

答案 0 :(得分:1)

document.addEventListener('DOMContentLoaded', function(){ var buyButton = document.getElementById('buy'); buyButton.disabled = false; //or true }, false); 模块在​​其实现uses functools.wraps中调用<form action=""> <input type='submit' name='buy' value='Buy' disabled='false' id='buy'/> </form> <script> var buyButton = document.getElementById('buy'); buyButton.disabled = false; //or true </script> ,您可以看到from the source code默认情况下会更新包装器的backoff:< / p>

functools.update_wrapper

不幸的是,似乎无法达到你想要的效果。 __dict__模块可以允许将可选的WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__', '__annotations__') WRAPPER_UPDATES = ('__dict__',) def update_wrapper(wrapper, wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES): """Update a wrapper function to look like the wrapped function wrapper is the function to be updated wrapped is the original function assigned is a tuple naming the attributes assigned directly from the wrapped function to the wrapper function (defaults to functools.WRAPPER_ASSIGNMENTS) updated is a tuple naming the attributes of the wrapper that are updated with the corresponding attribute from the wrapped function (defaults to functools.WRAPPER_UPDATES) """ for attr in assigned: try: value = getattr(wrapped, attr) except AttributeError: pass else: setattr(wrapper, attr, value) for attr in updated: getattr(wrapper, attr).update(getattr(wrapped, attr, {})) + −# Issue #17482: set __wrapped__ last so we don't inadvertently copy it # from the wrapped function when updating __dict__ wrapper.__wrapped__ = wrapped # Return the wrapper so this can be used as a decorator via partial() return wrapper def wraps(wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES): """Decorator factory to apply update_wrapper() to a wrapper function Returns a decorator that invokes update_wrapper() with the decorated function as the wrapper argument and the arguments to wraps() as the remaining arguments. Default arguments are as for update_wrapper(). This is a convenience function to simplify applying partial() to update_wrapper(). """ return partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated) / backoff属性列表传递给assigned,以避免复制属性。但是,这确实可以解决问题,因为此时您将无法访问updated

您可能需要使用可变对象而不是普通wraps