简体   繁体   English

如何不在 Python 中将数据类成员实例化为类变量?

[英]How to not instantiate dataclass members as class variables in Python?

By default, it seems that Python considers the class members as ClassVar.默认情况下,Python 似乎将类成员视为 ClassVar。 Although from the documentation: https://docs.python.org/3/library/dataclasses.html#class-variables虽然来自文档: https : //docs.python.org/3/library/dataclasses.html#class-variables

One of two places where dataclass() actually inspects the type of a field is to determine if a field is a class variable as defined in PEP 526. It does this by checking if the type of the field is typing.ClassVar. dataclass() 实际检查字段类型的两个地方之一是确定字段是否是 PEP 526 中定义的类变量。它通过检查字段的类型是否为 Typing.ClassVar 来实现。 If a field is a ClassVar, it is excluded from consideration as a field and is ignored by the dataclass mechanisms.如果字段是 ClassVar,则将其作为字段排除在外,并被数据类机制忽略。 Such ClassVar pseudo-fields are not returned by the module-level fields() function.模块级 fields() 函数不会返回此类 ClassVar 伪字段。

A minimal reproduction of the issue:问题的最小再现:

from dataclasses import dataclass
import numpy as np

@dataclass
class Color:
    r: int = 0
    g: int = 0
    b: int = 0

@dataclass
class NumberColor:
    color: Color = Color()
    number: int = 0
    alpha = np.zeros(3)

x = NumberColor()
y = NumberColor()

print("Id of x:", id(x))
print("Id of y:", id(y))
print('-----------')
print("Id of x.color:", id(x.color))
print("Id of y.color:", id(y.color))
print('-----------')
print("Id of x.number:", id(x.number))
print("Id of y.number:", id(y.number))
print('-----------')
print("Id of x.alpha:", id(x.alpha))
print("Id of y.alpha:", id(y.alpha))
print('-----------')

x.color.r = 255
x.number = 10

print(x.__dict__)
print(y.__dict__)

Yields:产量:

Id of x: 140660354709392
Id of y: 140660357249008
-----------
Id of x.color: 140660355994096
Id of y.color: 140660355994096
-----------
Id of x.number: 9788960
Id of y.number: 9788960
-----------
Id of x.alpha: 140660289932624
Id of y.alpha: 140660289932624
-----------
{'color': Color(r=255, g=0, b=0), 'number': 10}
{'color': Color(r=255, g=0, b=0), 'number': 0}

No, the color attribute of your NumberColor class isn't being considered a class variable.不,您的NumberColor类的color属性不被视为类变量。 Rather, it's a shared default argument to the __init__ method that's being created for your class by the dataclass decorator.相反,它是由dataclass装饰器为您的类创建的__init__方法的共享默认参数。

It's not very much different than any other issue with a mutable default argument:它与可变默认参数的任何其他问题没有太大不同:

def foo(c = Color()): # default value is created here
    return c

x = foo()    # get the default value
print(x.r)   # 0, as is expected
x.r = 255

y = foo()    # get the default again
print(y.r)   # 255, not 0 as you might naively expect

In your code, the equivalent to x and y are the x.color and y.color attributes.在您的代码中,与xy等效的是x.colory.color属性。

To avoid this issue, you can assign a dataclasses.field object to the name in the class body with a default_factory argument that is the Color class (you'll also want to do something similar for the numpy array used as the default for alpha ):为避免此问题,您可以使用作为Color类的default_factory参数将dataclasses.field对象分配给类主体中的名称(您还需要对用作alpha的默认值的 numpy 数组执行类似操作) :

from dataclasses import dataclass, field

@dataclass
class NumberColor:
    color: Color = field(default_factory=Color)         # use a field!
    number: int = 0
    alpha = field(default_factory=lambda: np.zeros(3))  # here too

There's no need to use field for number (or for the attributes in Color ) because int objects are immutable, and you shouldn't care if the same object is being used for any given 0 value.不需要将field用于number (或用于Color的属性),因为int对象是不可变的,并且您不应该关心是否将相同的对象用于任何给定的0值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM