问题描述
我有一个可呼叫的班级:
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
.
这篇关于装饰的可调用类上的属性访问的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!