gpt4 book ai didi

python - 修饰的可调用类上的属性访问

转载 作者:行者123 更新时间:2023-12-01 09:30:16 24 4
gpt4 key购买 nike

我有一个可调用的类:

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

最佳答案

其实现中的backoff模块uses functools.wraps它调用 functools.update_wrapper ,您可以看到 from the source code默认情况下它会更新包装器的 __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 模块可以允许将可选的分配/更新属性列表传递给wraps以避免复制属性。然而,这确实可以解决问题,因为那时您将无法访问n_calls

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

关于python - 修饰的可调用类上的属性访问,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50030408/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com