【问题标题】:Dataclass - why attributes are treated differently from normal classes?Dataclass - 为什么属性的处理方式与普通类不同?
【发布时间】:2021-04-17 21:14:43
【问题描述】:

我将dataclass 理解为“自动”创建__init____repr__ 和其他功能的装饰器。

但我注意到了一些令我意想不到的事情,我会知道这是否是预期的,因为我在官方文档中找不到任何相关的内容(至少我没有相关)

第一个例子:

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

我的三个问题是:

  1. 为什么在检查object_1.iobject_2.i 时更改Normal.i 与更改Data.i 不同
  2. 为什么Data.s 已更改但object_4.s 未更改
  3. 此行为在文档中的哪个位置进行了说明?

我的猜测是,使用装饰器会导致返回对__new__ 运算符的引用并更改定义中的值应该是这种方式。 但是我在文档中找不到它的说明,所以我很困惑。

有人知道吗?

【问题讨论】:

  • 因为在class Normalfs 中是类属性

标签: python python-dataclasses


【解决方案1】:

简而言之,@dataclass 装饰器通过从类型注释中提取变量来转换类的定义。在找不到文档时了解发生了什么的最佳方法是查看源代码。

我们可以先去dataclassdefinition,看看它返回了一个_process_class()处理的类。在函数里面,你可以发现它gives a new initializer到被装饰的类,这基本上是你猜到的。

正如@juanpa.arrivillaga 所指出的,您的Normal.iData.i 不同的原因是Data.i@dataclass 的对象属性,而您的Normal.i 是类属性。这也是设置Data.s 对您的object_4.s 没有影响的原因。

最后,这种行为并没有在文档本身中详细说明,而是在链接的PEP557 中,其中说明了添加@dataclass 的确切效果。

【讨论】:

    猜你喜欢
    • 2016-03-25
    • 1970-01-01
    • 2019-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-17
    • 2021-05-21
    相关资源
    最近更新 更多