When the method __post_init__ doesnt called?

Question:

I found the issue where was conversation about an explict call of parent’s __post_init__ method using super(), but if I try this:

from dataclasses import dataclass


@dataclass
class A:
    def __post_init__(self):
        print("A")


@dataclass
class B(A):
    pass


b = B()

it will output:

A

So, parents method works without explict call.

  1. I don’t understand what talk about was in this issue or problem with inheritence was solved?
  2. In which case __post_init__ method of dataclass doesnt called?
Asked By: Maksim Burtsev

||

Answers:

By default, the dataclass decorator generates an __init__ method which does the following (among other things):

  • Accepts input arguments based on the field definitions in the current and parent classes
  • Initializes the attributes of self from input arguments
  • Calls self.__post_init__

Python’s inheritance works such that if a parent class has an attribute, the child inherits it unless told otherwise. That’s sort of the point.

To disable inheritance, you must override it somehow. The pythoic approach to disabling an inherited method entirely is to set it to None in the child class:

@dataclass
class B(A):
    __post_init__ = None

You can find this technique in the documentation of NotImplementedError, which you should not be using here:

Note: It [NotImplementedError] should not be used to indicate that an operator or method is not meant to be supported at all – in that case either leave the operator / method undefined or, if a subclass, set it to None.

In fact, any time the child class overrides a method, the parent implementation won’t be called unless explicitly told to do so.

In other words, setting the child’s method to None is roughly equivalent to

@dataclass
class B(A):
    def __post_init__(self):
        pass

While leaving the name out of the child class is very roughly equivalent to

@dataclass
class B(A):
    def __post_init__(self):
        super().__post_init__()

By not calling super().__post_init__() from within the child implementation, you prevent the parent implementation from ever being executed.

Answered By: Mad Physicist

The issue under discussion is in how __init__ and __post_init__ differ.

object defines __init__; it doesn’t do anything, but that means it’s always safe to call super().__init__ in your own definitions of __init__. You have to take care what arguments you pass to it, but the attribute lookup itself will always succeed.

object does not define __post_init__, so calling super().__post_init__ requires you to know whether at least one class in your MRO will define it, or that you check for it first, or you’ll get an AttributeError.

So __post_init__, if defined, will always be called, but it’s not safe to assume for explicit super().__post_init__ calls that the method exists.

Answered By: chepner