数据类 - 为什么属性的处理方式与正常处理方式不同 类?
Dataclass - why attributes are treated differently from normal classes?
我理解 dataclass
作为创建 __init__
和 __repr__
以及其他函数的装饰器 'automatically'.
但我注意到了一些出乎我意料的事情,我想知道这是否是预期的事情,因为我在官方文档中找不到任何相关内容(至少不是我相关的)
第一个例子:
from dataclasses import dataclass
class CustomObj():
def __init__(self, x):
self.x = x
print(f'called custom obj with {x}')
class Normal():
i : int
o : CustomObj
f : float = 100.
s : str = 'this is a string'
@dataclass
class Data():
i : int
o : CustomObj
f : float = 100.
s : str = 'this is a string'
object_1 = Normal()
object_2 = Data(i = 1., o = CustomObj('custom_from_2'))
try:
object_1.i
except AttributeError:
print('it is ok, detecting an expected attribute error')
assert object_2.i == 1.
print('it is ok, because dataclass makes us set i value')
assert object_1.f == object_2.f
print('it is ok, because it is an "native" value')
try:
object_1.o
except AttributeError:
print('it is ok, detecting an expected attribute error, we didnt set o for object_1')
assert Normal.f == Data.f
print('it is ok, both classes have the same values and we did mess with it yet')
object_1.o = CustomObj('custom_from_1')
print('we set a customObj for obj_1 here')
Normal.f = 222.
Data.f = 222.
assert Normal.f == Data.f
print('it is ok, we set both values to same thing')
assert object_1.f == 222 and object_2.f == 100.
print(f'1: {object_1.f} 2: {object_2.f}')
print('By setting Normal.f we set object_1.f to 222 but object_2.f still 100')
object_1.s = 'changing object_1.s to something else'
object_2.s = 'changing object_2.s to something else'
Normal.s = 'changing Normal.s to something else'
Data.s = 'changing Data.s to something else'
print(object_1.s, object_2.s)
print(Normal.s, Data.s)
object_3 = Normal()
object_4 = Data(i = 4., o = CustomObj('custom_from_4'))
assert object_3.s == 'changing Normal.s to something else'
print('it is expected to have new value for the class definitions of Normal here')
print(f'Normal.s: {Normal.s}')
print(f'Data.s: {Data.s}')
print(f'object_1.s: {object_1.s}')
print(f'object_2.s: {object_2.s}')
print(f'object_3.s: {object_3.s}')
print(f'object_4.s: {object_4.s}')
输出为:
called custom obj with custom_from_2
it is ok, detecting an expected attribute error
it is ok, because dataclass makes us set i value
it is ok, because it is an "native" value
it is ok, detecting an expected attribute error, we didnt set o for object_1
it is ok, both classes have the same values and we did mess with it yet
called custom obj with custom_from_1
we set a customObj for obj_1 here
it is ok, we set both values to same thing
1: 222.0 2: 100.0
By setting Normal.f we set object_1.f to 222 but object_2.f still 100
changing object_1.s to something else changing object_2.s to something else
changing Normal.s to something else changing Data.s to something else
called custom obj with custom_from_4
it is expected to have new value for the class definitions of Normal here
Normal.s: changing Normal.s to something else
Data.s: changing Data.s to something else
object_1.s: changing object_1.s to something else
object_2.s: changing object_2.s to something else
object_3.s: changing Normal.s to something else
object_4.s: this is a string
我的三个问题是:
- 为什么在检查
object_1.i
和object_2.i
时更改Normal.i
与更改Data.i
不同
- 为什么
Data.s
变了而 object_4.s
没有变
- 此行为在文档中的什么位置?
我的猜测是,使用装饰器会返回对某种 __new__
运算符的引用,并且应该以这种方式更改定义中的值。
但是我在文档中找不到它的说明,所以我很困惑。
有人知道吗?
简而言之,@dataclass
装饰器通过从类型注释中提取变量来转换 class 的定义。当您找不到文档时,了解发生了什么的最好方法是查看源代码。
我们可以先去definition of dataclass
and see that it returns a class processed by _process_class()
. Inside the function, you can find that it gives a new initializer到正在装修的class,基本就是你猜到的
正如@juanpa.arrivillaga 所指出的,您的 Normal.i
与 Data.i
不同的原因是因为 Data.i
是 @dataclass
object 属性,而你的 Normal.i
是 class 属性。这也是设置 Data.s
对您的 object_4.s
.
没有影响的原因
最后,此行为在文档本身中并未详细阐述,但在链接 PEP557 中,其中说明了添加 @dataclass
.
的确切效果
我理解 dataclass
作为创建 __init__
和 __repr__
以及其他函数的装饰器 'automatically'.
但我注意到了一些出乎我意料的事情,我想知道这是否是预期的事情,因为我在官方文档中找不到任何相关内容(至少不是我相关的)
第一个例子:
from dataclasses import dataclass
class CustomObj():
def __init__(self, x):
self.x = x
print(f'called custom obj with {x}')
class Normal():
i : int
o : CustomObj
f : float = 100.
s : str = 'this is a string'
@dataclass
class Data():
i : int
o : CustomObj
f : float = 100.
s : str = 'this is a string'
object_1 = Normal()
object_2 = Data(i = 1., o = CustomObj('custom_from_2'))
try:
object_1.i
except AttributeError:
print('it is ok, detecting an expected attribute error')
assert object_2.i == 1.
print('it is ok, because dataclass makes us set i value')
assert object_1.f == object_2.f
print('it is ok, because it is an "native" value')
try:
object_1.o
except AttributeError:
print('it is ok, detecting an expected attribute error, we didnt set o for object_1')
assert Normal.f == Data.f
print('it is ok, both classes have the same values and we did mess with it yet')
object_1.o = CustomObj('custom_from_1')
print('we set a customObj for obj_1 here')
Normal.f = 222.
Data.f = 222.
assert Normal.f == Data.f
print('it is ok, we set both values to same thing')
assert object_1.f == 222 and object_2.f == 100.
print(f'1: {object_1.f} 2: {object_2.f}')
print('By setting Normal.f we set object_1.f to 222 but object_2.f still 100')
object_1.s = 'changing object_1.s to something else'
object_2.s = 'changing object_2.s to something else'
Normal.s = 'changing Normal.s to something else'
Data.s = 'changing Data.s to something else'
print(object_1.s, object_2.s)
print(Normal.s, Data.s)
object_3 = Normal()
object_4 = Data(i = 4., o = CustomObj('custom_from_4'))
assert object_3.s == 'changing Normal.s to something else'
print('it is expected to have new value for the class definitions of Normal here')
print(f'Normal.s: {Normal.s}')
print(f'Data.s: {Data.s}')
print(f'object_1.s: {object_1.s}')
print(f'object_2.s: {object_2.s}')
print(f'object_3.s: {object_3.s}')
print(f'object_4.s: {object_4.s}')
输出为:
called custom obj with custom_from_2
it is ok, detecting an expected attribute error
it is ok, because dataclass makes us set i value
it is ok, because it is an "native" value
it is ok, detecting an expected attribute error, we didnt set o for object_1
it is ok, both classes have the same values and we did mess with it yet
called custom obj with custom_from_1
we set a customObj for obj_1 here
it is ok, we set both values to same thing
1: 222.0 2: 100.0
By setting Normal.f we set object_1.f to 222 but object_2.f still 100
changing object_1.s to something else changing object_2.s to something else
changing Normal.s to something else changing Data.s to something else
called custom obj with custom_from_4
it is expected to have new value for the class definitions of Normal here
Normal.s: changing Normal.s to something else
Data.s: changing Data.s to something else
object_1.s: changing object_1.s to something else
object_2.s: changing object_2.s to something else
object_3.s: changing Normal.s to something else
object_4.s: this is a string
我的三个问题是:
- 为什么在检查
object_1.i
和object_2.i
时更改 - 为什么
Data.s
变了而object_4.s
没有变 - 此行为在文档中的什么位置?
Normal.i
与更改Data.i
不同
我的猜测是,使用装饰器会返回对某种 __new__
运算符的引用,并且应该以这种方式更改定义中的值。
但是我在文档中找不到它的说明,所以我很困惑。
有人知道吗?
简而言之,@dataclass
装饰器通过从类型注释中提取变量来转换 class 的定义。当您找不到文档时,了解发生了什么的最好方法是查看源代码。
我们可以先去definition of dataclass
and see that it returns a class processed by _process_class()
. Inside the function, you can find that it gives a new initializer到正在装修的class,基本就是你猜到的
正如@juanpa.arrivillaga 所指出的,您的 Normal.i
与 Data.i
不同的原因是因为 Data.i
是 @dataclass
object 属性,而你的 Normal.i
是 class 属性。这也是设置 Data.s
对您的 object_4.s
.
最后,此行为在文档本身中并未详细阐述,但在链接 PEP557 中,其中说明了添加 @dataclass
.