0

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:

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?

DHJ
  • 611
  • 2
  • 13
  • `nested = Test()` creates a *class* attribute that is a specific `Test` instance. It is never recreated. Use a factory if you want a new attribute per instance. – MisterMiyagi Apr 21 '22 at 14:04

1 Answers1

3

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).

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271