[英]python decorated dataclass method is shared for all instances
I'm implementing a python decorator with an internal memory (represented by counter
below).我正在实现一个带有内部存储器的 python 装饰器(由下面的
counter
表示)。
It seams the decorator variables are shared across instances of a dataclass
while being different for instances of a common class
.它接缝装饰器变量在
dataclass
实例之间共享,而对于公共class
实例则不同。
Why is so?为什么会这样? And is there a cleaner/easier solution other than checking if
f
belongs to a class or not and if so, if the class is a dataclass
?除了检查
f
属于一个类以及如果是这样,如果该类是一个dataclass
,是否还有更清洁/更简单的解决方案?
import dataclasses
def decorator(f):
counter = {}
def wrapper(*args, **kwargs):
key = repr([f.__name__, args, kwargs])
counter[key] = counter.setdefault(key, 0)+1
result = f(*args, **kwargs)
print(f"{counter[key]}", end=" ")
return result
return wrapper
@dataclasses.dataclass
class D:
@decorator
def foo(self):
pass
class C:
@decorator
def foo(self):
pass
Despide C
and D
being very similar, the code below shows that the instances of normal object
have different counter
each:尽管
C
和D
非常相似,但下面的代码显示普通object
的实例每个都有不同的counter
:
>>> for i in range(5):
... c = C()
... c.foo()
1 1 1 1 1
While when using the dataclass
instead, the counter
is shared:而当使用
dataclass
, counter
是共享的:
>>> for i in range(5):
... c = D()
... c.foo()
1 2 3 4 5
Decorator syntax is a shortcut for function application, so each use of @decorator
is a separate call to decorator
, each of which creates a new dict
associated with the decorated function.装饰器语法是函数应用的快捷方式,因此每次使用
@decorator
都是对decorator
的单独调用,每次调用都会创建一个与装饰函数关联的新dict
。
So it's one counter per decorated function, and there is one decorated function per class in your example.所以每个装饰函数有一个计数器,并且在您的示例中每个类有一个装饰函数。
But then there's another problem.但接下来还有另一个问题。 Your key depends on each class's
__repr__
function, as *args
includes the object itself.您的密钥取决于每个类的
__repr__
函数,因为*args
包括对象本身。
For C
, __repr__
is not defined, so object.__repr__
is used, producing a unique key for each instance.对于
C
, __repr__
未定义,因此使用object.__repr__
,为每个实例生成唯一键。
For D
, D.__repr__
returns a generic string 'D()'
for every instance, so you aren't getting unique keys for instances of D
.对于
D
, D.__repr__
为每个实例返回一个通用字符串'D()'
,因此您不会获得D
实例的唯一键。
The solution is to be more explicit in constructing the key.解决方案是在构造密钥时更加明确。 Perhaps something like
也许像
from collections import Counter
def decorator(f):
counter = Counter()
def wrapper(*args, **kwargs):
key = repr([id(f.__name__), [id(x) for x in args], [id(x) for x in kwargs.items()]])
counter[key] += 1
result = f(*args, **kwargs)
# print(f"{counter[key]}", end=" ")
return result
return wrapper
As you're decorating methods rather than functions, the value of *args
in wrapper(*args, **kwargs)
will be a one element tuple containing the implicit self
.当您装饰方法而不是函数时,
wrapper(*args, **kwargs)
中*args
的值将是一个包含隐式self
的单元素元组。
Your key
value would then look like this ['foo', (<__main__.C object at 0x7fe8945403c8>,), {}]
.然后,您的
key
将如下所示['foo', (<__main__.C object at 0x7fe8945403c8>,), {}]
。
As those instances of C
and D
get garbage collected, sometimes Python will reuse the same memory address and sometimes not, leading to different keys.当
C
和D
那些实例被垃圾收集时,有时 Python 会重用相同的内存地址,有时不会,导致不同的键。
I'm not sure why Python would reuse dataclass addresses more than for regular classes.我不确定为什么 Python 会比常规类更多地重用数据类地址。
If you change wrapper
to expect self
, you should get consistent results.如果您将
wrapper
更改为期望self
,您应该获得一致的结果。
def decorator(f):
counter = {}
def wrapper(self, *args, **kwargs):
key = repr([f.__name__, args, kwargs])
counter[key] = counter.setdefault(key, 0)+1
result = f(self, *args, **kwargs)
print(f"{counter[key]}", end=" ")
return result
return wrapper
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.