装饰的可调用类上的属性访问

装饰的可调用类上的属性访问

本文介绍了装饰的可调用类上的属性访问的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个可呼叫的班级:

I have a callable class:

class CallMeMaybe:

    __name__ = 'maybe'

    def __init__(self):
        self.n_calls = 0

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

这似乎与广告宣传的一样:

That seems to work as advertised:

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

我想用指数:

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

现在看来属性访问已停止:

Now it looks like attribute access stopped working:

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

My question: who copied n_calls name into f2's namespace, and why? Now it holds a stale value - the correct value should be 3:

>>> f2.__wrapped__.n_calls
3


推荐答案

backoff 模块在其实现中调用 functools.update_wrapper ,您会看到,默认情况下,它会更新<$ c包装器的$ c> __ dict __ :

The backoff module in its implementation uses functools.wraps which calls functools.update_wrapper and you can see from the source code that by default it updates the __dict__ of the wrapper:

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 模块可以允许可选的已分配 / 已更新属性列表,这些属性列表将传递给包装避免复制属性。但这确实可以解决问题,因为在那时您将无法访问 n_calls


Unfortunately it seems impossible to achieve what you want. The backoff module could allow optional assigned/updated attributes lists to be passed to wraps to avoid the copy of the attribute. However this would really solve the issue because at that point you would not have access to n_calls.

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

You probably need to use a mutable object instead of a plain int.

这篇关于装饰的可调用类上的属性访问的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-20 11:16