@dataclasses.dataclass
classes.I'm trying to do a simple nesting of multiple Python @dataclass
decorated classes within another class, and have later classes refer back to the earlier ones. If I do not nest them at all, they work as expected, being able to include the first class defined into an object in the second class:
from dataclasses import dataclass, field
@dataclass
class A:
z:int = field(default=0)
@dataclass
class B:
a:A = field(default=A(z=1)) ### Object that is class A is included in class B
b = B(a=A(z=3))
print(f'b = B(a=A(z=3)); b.a.z={b.a.z}; b={b}')
But if I try to do the same inside of another class (in this case, not a dataclass), the "B" class cannot see the "A" class. In the below code, the definition of a
as a type of A
fails with a NameError: "name A
is not defined". I've tried A and C.A, neither work.
Note that the other functions in the C class are able to see both A and B just fine, just inside dataclass B cannot see dataclass A.
class C:
@dataclass
class A:
z:int = field(default=0)
@dataclass
class B:
a:A = field(default=A(z=1)) ### NameError: name 'A' is not defined
def __init__(self):
self.b = C.B(a=C.A(z=3))
def print_info(self):
print(f'b = C.B(a=C.A(z=3)); b.a.z={self.b.a.z}; b={b}')
c = C()
c.print_info()
However, if I convert these to normal Python classes, it works in the nested case:
Rechecking, it turns out this is broken in normal classes as well (per comment below).
Strangely, if one nests dataclass A inside dataclass B, with B still inside class C, it does work - B has direct access to A, but nothing else in class C has direct access to A.
Is it possible to define nested dataclasses with having the later ones access the earlier ones at the same level? If so, how?
To my best understanding, this is due to the semantics for class definitions – emphasis mine:
The class’s suite is then executed in a new execution frame, using a newly created local namespace and the original global namespace. (Usually, the suite contains mostly function definitions.) When the class’s suite finishes execution, its execution frame is discarded but its local namespace is saved.
That is, any class definition only ever has the module-global namespace (which does not yet contain C
in this case, since its suite hasn't finished executing) and a new empty local namespace.
Based on the above, this can be hacked together to work, but I really wouldn't do this...
from dataclasses import dataclass, field
class C:
@dataclass
class A:
z: int = field(default=0)
globals()["A"] = A # "and the original global namespace..."
@dataclass
class B:
a: A = field(default=A(z=1))
def __init__(self):
self.b = C.B(a=C.A(z=3))
def print_info(self):
print(f"{self.b.a.z=}")
c = C()
c.print_info()