When you add a field to a python dataclass, it will become a required argument for __init__
and it will be an instance attribute. But when you add a default value to a field it becomes a class attribute.
from dataclasses import dataclass
@dataclass
class A:
foo: int
bar: int = 0
>>> A.foo
AttributeError: type object 'A' has no attribute 'foo'
>>> A.bar
0
Why this difference between foo
and bar
?
Dataclass prevents you from setting mutable defaults, so it will not become a problem in practice, but I was curious about this behaviour, which was unexpected for me.
You've got things backward. Setting a field default doesn't make it a class attribute. Setting a class attribute, and annotating it, causes the @dataclass
decorator to process that attribute as the default value of the field with that name.
When you write
@dataclass
class A:
foo: int
bar: int = 0
what bar: int = 0
means is that you're creating a class attribute named bar
with value 0
, and creating an annotation of int
for that attribute.
@dataclass
will look at that and generate an __init__
that sets bar
to 0
on an instance if no other value is provided, so there will be a bar
instance attribute on instances you create. But the class attribute is still there too.
There's a bit more to it than this, because if you create field objects:
@dataclass
class A:
foo: int = field()
bar: int = field(default=0)
this is still setting class attributes, but @dataclass
won't just leave the field objects there. A field with no default will be deleted from the class attributes, while a field with a default will be replaced with its default value. So @dataclass
doesn't quite just leave all your class attributes there. It goes to deliberate effort to make sure defaults are class attributes, even in cases with field
where they wouldn't be.