Search code examples
pythonselfpython-dataclasses

How to reference `self` in dataclass' fields?


I am trying to do the equivalent of:

class A:
    def __init__(self):
        self.b = self.get_b()

    def get_b(self):
        return 1

using @dataclass. I want to use a @dataclass here because there are other (omitted) fields initialized from provided constructor arguments. b is one of the fields that is initialized from an instance method's result. Executing this:

@dataclass
class A:
    b = self.get_b()

    def get_b(self):
        return 1

shows that self is not defined in b's scope. Is it possible to get a reference to self here?


Solution

  • Use the __post_init__ method.

    from dataclasses import dataclass, field
    
    
    @dataclass
    class A:
        b: int = field(init=False)
    
        def __post_init__(self):
            self.b = self.get_b()
    

    Not exactly an improvement, is it? The default value assigned to a field is equivalent to a default parameter value in __init__, e.g.,

    def __init__(self, b=0):
        self.b = b
    

    Just like you can't use self in such a parameter default, you can't use it as the field default value. The default value has to exist before the instance actually exists.

    We create the field explicitly so that we can pass init=False, preventing the generated __init__ method from expecting an argument to initialize self.b.


    If the function get_b is really independent of an instance (that is, you don't need an instance of A in order to return the value 1), you can use an already defined function as a default factory.

    from dataclasses import dataclass, field
    
    
    @dataclass
    class A:
        def get_b(self=None):
            return 1
    
        b: int = field(default_factory=get_b, init=False)
    

    Here, get_b will be called as a function with zero arguments (hence the default value for self in the definition) rather than be invoked from a class instance. This is, though, rather unorthodox and I wouldn't recommend structuring your code like this.