Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Confussion on nested dataclasses

Consider the following class:

from dataclasses import dataclass

@dataclass
class Test:
    result: int = None
    
@dataclass
class Test2:
    nested = Test()
    result: int = None
    

class Mainthing:
    def __init__(self, value):
        self.value = value
        self.results = Test2()
        
    def Main(self):
        value = self.value
        self.results.result = value   
        self.results.nested.result = value

If I make an instance of the class:

x = Mainthing(1)

And call the Main() function:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

x.Main()

The results are as they should be,

x.results.result
Out[0]: 1
x.results.nested.result
Out[1]: 1

If I then delete the instance

del x

and make it again

x = Mainthing(1)

The x.results.result is now None as i would expect, but the nested is not

x.results.nested.result
Out[]: 1

Why is that?

>Solution :

Without a type annotation, nested is a class attribute, unused by @dataclass, which means it’s shared between all instances of Test2 (dataclasses.fields(Test2) won’t report it, because it’s not an instance attribute).

Making it an instance attribute alone, with:

nested: Test = Test()

might seem like it works (it does make it a field), but it’s got the same problem using mutable default arguments has everywhere; it ends up being a shared default, and mutations applied through one instance change the value shared with everyone else.

If you want to use a new Test() for each instance, make it a field with a default_factory, adding field to the imports from dataclasses, and changing the definition to, e.g.:

nested: Test = field(default_factory=Test)

If you always want to create your own instance, never accept one from the user, make it:

nested: Test = field(default_factory=Test, init=False)

so the generated __init__ does not accept it (accepting only result).

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading