0% found this document useful (0 votes)
2 views18 pages

Python Init, New, Subclassing

__init_subclass__ is a class method that runs when a class is defined, allowing for declarative configuration through keyword arguments (**kwargs). It captures any keyword arguments passed during class definition, enabling features like automatic registration and enforcing naming conventions. Always forward kwargs to super() to maintain compatibility in multiple inheritance scenarios.

Uploaded by

Jimmy Hopkins
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views18 pages

Python Init, New, Subclassing

__init_subclass__ is a class method that runs when a class is defined, allowing for declarative configuration through keyword arguments (**kwargs). It captures any keyword arguments passed during class definition, enabling features like automatic registration and enforcing naming conventions. Always forward kwargs to super() to maintain compatibility in multiple inheritance scenarios.

Uploaded by

Jimmy Hopkins
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

User:

You are an experienced Python Programmer and a Princeton CS professor.


Explain the __init__ method in python and how it relates to __new__.
Also, consider a inheritance chain: explain how the presence of __init__ in the
superclass affects __init__ in subclasses. Moreover, how can a superclass
declare that its subclasses must have certain attributes and methods?

Kimi:
__init__ and __new__ in Python
1. __init__ vs. __new__
The distinction between __init__ and __new__ is one of the most elegant yet
commonly misunderstood aspects of Python’s object model.
__new__ — The Constructor
__new__ is the actual constructor. It is responsible for creating and returning a
new instance. It is a static method (though declared without @staticmethod) that
receives the class itself as its first argument:
class Foo:
def __new__(cls, *args, **kwargs):
print(f"Creating instance of {cls.__name__}")
instance = super().__new__(cls) # delegate to
object.__new__
return instance # MUST return an instance

Key properties: - Receives cls (the class), not self - Must return an instance
(usually of cls, but not necessarily—this enables metaprogramming tricks and
subclassing immutable types) - Runs before __init__
__init__ — The Initializer
__init__ is the initializer. It receives the already-created instance and
configures its state:
def __init__(self, value):
[Link] = value # 'self' is the instance returned by
__new__

Generated by [Link]
Key properties: - Receives self (the instance) - Must return None implicitly
(returning anything else raises TypeError) - Cannot prevent instance creation—
by the time __init__ runs, the object already exists
When to Override __new__
You rarely need __new__. The two canonical cases are:
1. Subclassing immutable built-in types (where you cannot modify state
after creation):
class PositiveInt(int):
def __new__(cls, value):
if value < 0:
raise ValueError("Value must be positive")
return super().__new__(cls, value)

2. Implementing singletons or object pooling (though there are usually


better patterns).

2. Inheritance and __init__


The Default Behavior
If a subclass does not define __init__, it inherits the superclass’s __init__
entirely. This means the subclass is implicitly initialized exactly like its parent:
class Animal:
def __init__(self, name):
[Link] = name

class Dog(Animal):
pass # Inherits Animal.__init__ exactly

d = Dog("Fido") # Works fine; [Link] == "Fido"

The Shadowing Problem


Once a subclass defines its own __init__, it shadows (overrides) the
superclass’s __init__. The parent’s initializer is not called automatically:
class Animal:
def __init__(self, name):
[Link] = name

class Dog(Animal):
def __init__(self, name, breed):
[Link] = breed # Oops! [Link] is never set!

Generated by [Link]
d = Dog("Fido", "Labrador")
print([Link]) # "Labrador"
print([Link]) # AttributeError!

Explicit Delegation with super()


You must explicitly call the superclass initializer using super():
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # Delegate to Animal.__init__
[Link] = breed

The Diamond Problem and MRO


In multiple inheritance, super() does not simply call the parent—it follows the
Method Resolution Order (MRO). This is critical in cooperative multiple
inheritance:
class A:
def __init__(self):
print("A")
super().__init__()

class B(A):
def __init__(self):
print("B")
super().__init__()

class C(A):
def __init__(self):
print("C")
super().__init__()

class D(B, C):


def __init__(self):
print("D")
super().__init__()

print(D.__mro__) # (<class D>, B, C, A, object)


D() # Prints: D, B, C, A (in that order)

Notice that A.__init__ runs only once, despite appearing in two inheritance
chains. This is because super() is not “call my parent”—it is “call the next class
in the MRO.” For cooperative multiple inheritance to work, every __init__ in the
hierarchy must use super() and accept **kwargs to pass along arguments it
doesn’t consume.

Generated by [Link]
3. Enforcing Attributes and Methods in Subclasses
Python is dynamically typed, so “enforcement” is convention-based unless you
use specific mechanisms. Here are the rigorous approaches:
Abstract Base Classes (ABCs) — For Methods
Use the abc module to declare that a class is abstract and cannot be instantiated
unless all abstract methods are implemented:
from abc import ABC, abstractmethod

class Shape(ABC):
@abstractmethod
def area(self) -> float:
"""Calculate and return the area."""
raise NotImplementedError

@abstractmethod
def perimeter(self) -> float:
"""Calculate and return the perimeter."""
raise NotImplementedError

class Rectangle(Shape):
def __init__(self, width: float, height: float):
[Link] = width
[Link] = height

def area(self) -> float:


return [Link] * [Link]

def perimeter(self) -> float:


return 2 * ([Link] + [Link])

# s = Shape() # TypeError: Can't instantiate abstract class


Shape
r = Rectangle(3, 4) # Works

If Rectangle failed to implement area or perimeter, instantiation would raise


TypeError immediately.
__init_subclass__ — For Registration and Validation
Python 3.6+ provides __init_subclass__, a hook that runs when a class is
subclassed. You can use it to enforce attributes or register subclasses:
class Plugin(ABC):
required_attributes = ['version', 'author']

Generated by [Link]
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)

# Enforce required class attributes


for attr in cls.required_attributes:
if not hasattr(cls, attr):
raise TypeError(
f"Class {cls.__name__} must define class
attribute '{attr}'"
)

# Could also enforce methods


if not hasattr(cls, 'execute') or not
callable(getattr(cls, 'execute')):
raise TypeError(f"Class {cls.__name__} must implement
'execute' method")

class MyPlugin(Plugin):
version = "1.0"
author = "Alice"

def execute(self):
print("Running")

# class BadPlugin(Plugin): pass


# TypeError: Class BadPlugin must define class attribute
'version'

Protocols and Structural Subtyping (Python 3.8+)


With [Link], you can define an interface without requiring explicit
inheritance. This is “duck typing with teeth”:
from typing import Protocol, runtime_checkable

@runtime_checkable
class Drawable(Protocol):
def draw(self) -> None: ...

class Circle:
def draw(self) -> None:
print("Drawing circle")

def render(obj: Drawable) -> None:


[Link]()

render(Circle()) # OK

Generated by [Link]
Descriptors and Metaclasses — For Advanced Enforcement
For compile-time-like guarantees, metaclasses can inspect the class dictionary
during class creation:
class EnforceMeta(type):
def __new__(mcs, name, bases, namespace):
if 'process' not in namespace:
raise TypeError(f"{name} must implement 'process'")
return super().__new__(mcs, name, bases, namespace)

class Base(metaclass=EnforceMeta):
pass

class Good(Base):
def process(self): pass # OK

# class Bad(Base): pass # TypeError during class definition

Summary of Enforcement Mechanisms


Mechanism Use Case When It Runs
@abstractmethod Must implement Instantiation
method
__init_subclass__ Registration, Class definition of subclass
attribute checks
[Link] Structural interface Type-check time (mypy) or
checking runtime

Metaclass __new__ Deep structural Class definition


validation
The modern, Pythonic recommendation is to use ABCs for methods and
__init_subclass__ for attributes and registration, reserving metaclasses
only when you need to fundamentally alter class creation semantics.

User:
Expand on the __init_subclass__; provide an overview of its signature. Also,
your explained how __init_subclass can be used to ensure a subclass has

Generated by [Link]
class attributes, but not instance attributes/properties. Does it run when a
subclass is instantiated?

Kimi:
__init_subclass__ in Depth
Signature and Mechanics
__init_subclass__ is implicitly a class method. Its signature is:
def __init_subclass__(cls, **kwargs):
...

• cls: The subclass being created—not the base class that defines
__init_subclass__.

• **kwargs: Captures any keyword arguments passed inside the class


statement (e.g., class MyClass(Base, init=False):).
Because it is implicitly a class method, you do not decorate it with
@classmethod, and you do not need to pass cls manually.
class Base:
def __init_subclass__(cls, debug=False, **kwargs):
print(f"Subclass defined: {cls.__name__}, debug={debug}")
super().__init_subclass__(**kwargs) # Always forward
kwargs for MRO cooperation

class Child(Base, debug=True): # kwargs captured here


pass
# Output: Subclass defined: Child, debug=True

When Does It Run?


It runs when the subclass is defined, not when it is instantiated.
class Base:
def __init_subclass__(cls, **kwargs):
print(f"__init_subclass__ running for {cls.__name__}")
super().__init_subclass__(**kwargs)

print("Before class definition")


class Child(Base):
pass
print("After class definition")

c = Child() # Nothing prints here

Generated by [Link]
Output:
Before class definition
__init_subclass__ running for Child
After class definition

By the time you write c = Child(), __init_subclass__ has already executed


(and finished) during the class body compilation. It has no hook into object
instantiation.

Class Attributes vs. Instance Attributes


Because __init_subclass__ runs at class definition time, it can only inspect
what exists on the class object at that moment. This creates a fundamental
boundary:
What you can enforce What you cannot enforce

Class variables (x = 5) Variables assigned inside


__init__(self)

Methods Instance state that only exists after


__new__/__init__

Properties (descriptors defined at Attributes added dynamically to


class level) instances later

Class-level type hints Values stored in self.__dict__


Why Instance Attributes Cannot Be Verified
An instance attribute does not exist until an instance is created and __init__
runs. At class definition time, no instance exists, so there is no self to inspect:
class Base:
def __init_subclass__(cls, **kwargs):
# At this point, cls is the class object, not an instance
# There is NO way to check what cls.__init__ assigns to
self
# because __init__ hasn't been called yet.
super().__init_subclass__(**kwargs)

class Child(Base):
def __init__(self):
[Link] = 42 # This code hasn't run yet!

Generated by [Link]
What About Properties?
Properties are class-level descriptors, so __init_subclass__ can verify their
presence:
class Base:
def __init_subclass__(cls, **kwargs):
if not isinstance(getattr(cls, 'name', None), property):
raise TypeError(f"{cls.__name__} must define a 'name'
property")
super().__init_subclass__(**kwargs)

class Good(Base):
@property
def name(self):
return "good"

# class Bad(Base): pass


# TypeError: Bad must define a 'name' property

This works because @property creates a descriptor object on the class itself at
definition time.

Workarounds for Instance Attribute Enforcement


If you truly need to guarantee that instances carry certain attributes,
__init_subclass__ is the wrong tool. Use these instead:
1. Abstract Properties (Enforce at the Class Level)
Force subclasses to implement a property, which then reads from instance state:
from abc import ABC, abstractmethod

class Base(ABC):
@property
@abstractmethod
def value(self):
...

class Child(Base):
def __init__(self):
self._value = 42

@property
def value(self):
return self._value

Generated by [Link]
2. Override __init__ in the Base and Use super()
Template-method pattern: the base class defines __init__ and calls a hook:
class Base:
def __init__(self):
self._configure()

def _configure(self):
raise NotImplementedError("Subclasses must override
_configure")

class Child(Base):
def _configure(self):
[Link] = 42

3. __post_init__ or Descriptors with __set_name__


For advanced validation, a custom descriptor can intercept attribute assignment
on instances, but this is orthogonal to __init_subclass__.

Summary
• Signature: def __init_subclass__(cls, **kwargs): (implicit
classmethod)
• Execution timing: Runs once when the class statement is executed,
never during instantiation.
• Enforceable: Class attributes, methods, properties, and other descriptors.
• Not enforceable: Instance attributes assigned inside __init__, because
no instance exists at class definition time.

User:
In the __init_subclass__, what are kwargs useful for?

Kimi:
The **kwargs in __init_subclass__ captures keyword arguments passed
directly in the class statement. This is a powerful but underused feature that
enables declarative class configuration.

Generated by [Link]
What kwargs Actually Captures
When you write:
class MyClass(Base, option="value"):
pass

The keyword argument option="value" is not passed to Base.__init__


(that’s for instances). Instead, it is passed to Base.__init_subclass__ as part
of kwargs.

Practical Use Cases


1. Declarative Configuration
You can configure subclass behavior at the point of definition, without needing a
separate decorator or metaclass:
class Serializer:
def __init_subclass__(cls, model=None, **kwargs):
if model is None:
raise TypeError("Serializer subclass must specify a
model")
[Link] = model
super().__init_subclass__(**kwargs)

class UserSerializer(Serializer, model="User"):


pass

print([Link]) # "User"

2. Automatic Registration with Metadata


Frameworks often use this to register handlers with metadata:
class EventHandler:
_registry = {}

def __init_subclass__(cls, event_type=None, **kwargs):


if event_type:
EventHandler._registry[event_type] = cls
super().__init_subclass__(**kwargs)

class LoginHandler(EventHandler, event_type="[Link]"):


def handle(self, data):
print(f"Login: {data}")

print(EventHandler._registry)
# {"[Link]": <class LoginHandler>}

Generated by [Link]
3. Enforcing Naming Conventions or Schemas
class DatabaseModel:
def __init_subclass__(cls, table_name=None, **kwargs):
cls._table = table_name or cls.__name__.lower()
super().__init_subclass__(**kwargs)

class User(DatabaseModel, table_name="app_users"):


pass

print(User._table) # "app_users"

The Critical Rule: Always Forward kwargs


Because __init_subclass__ participates in the MRO just like any other
method, you must forward kwargs to super(). If you consume a keyword and
don’t forward the rest, any other base class in a multiple-inheritance chain that
also defines __init_subclass__ will break:
class PluginBase:
def __init_subclass__(cls, plugin_id=None, **kwargs):
cls.plugin_id = plugin_id
super().__init_subclass__(**kwargs) # CRITICAL: forward
remaining kwargs

class LoggingMixin:
def __init_subclass__(cls, log_level="INFO", **kwargs):
cls.log_level = log_level
super().__init_subclass__(**kwargs)

class MyPlugin(PluginBase, LoggingMixin, plugin_id="auth",


log_level="DEBUG"):
pass

print(MyPlugin.plugin_id) # "auth"
print(MyPlugin.log_level) # "DEBUG"

If PluginBase had consumed **kwargs without forwarding,


LoggingMixin.__init_subclass__ would never receive log_level, and
Python would raise:
TypeError: __init_subclass__() got an unexpected keyword argument
'log_level'

Generated by [Link]
What kwargs Is Not For
• It does not capture arguments passed to the subclass’s __init__ at
instantiation time.
• It does not capture class body contents (use metaclasses or
__set_name__ for that).

It is exclusively for keyword arguments written on the class inheritance line


itself.

User:
In sum: - __new__ creates an empty object in memory. __init__ fills it with
attributes and properties. - Subclasses inherent methods from their parent class
and won’t override unless explicitly defined. They still can call super().method.
- For ensuring that subclasses have instance attributes, define them with
@property and @abstractmethod decorators. - For ensuring that subclasses
define their own class attributes, __init_subclass should be utilized in the
parent class. It is not a runtime function, but rather run as definition time. -
__init_subclass__ is useful for class-level variables and behavior. Can be
employed to define custom class variables (ensuring consistency across
classes), or for registration.

Kimi:
Your summary is mostly accurate, but let me refine a few points for precision:

Corrections and Clarifications


__new__ Does Not Create “Empty” Objects
__new__ allocates memory and returns an instance, but it doesn’t necessarily
create an empty object. For immutable types (like int, str, tuple), __new__
must establish the final value because __init__ cannot modify the object
afterward. The instance is fully formed by __new__; __init__ only adds mutable
state to already-mutable objects.

Generated by [Link]
Inheritance of __init__ Is Shadowing, Not Optional
Subclasses inherit __init__ automatically, but once they define their own, the
parent’s __init__ is completely shadowed—not merely “available to call.” The
parent version does not run unless explicitly invoked via super().__init__().
This is a common source of bugs when developers assume inheritance chains
initialize automatically.
@property + @abstractmethod Enforces Interface, Not Instance Attributes
These decorators ensure subclasses implement a property getter at the class
level. They do not guarantee that any specific instance attribute exists in
self.__dict__—only that the property can be accessed. The underlying
storage mechanism (whether self._value, a descriptor, or computed on the fly)
remains the subclass’s choice.
__init_subclass__ Is for Class-Level Concerns, Not Variables Alone
You note it correctly: registration, validation of class-level structure, and
declarative configuration. But it is not limited to “class variables” in the narrow
sense—it also validates methods, properties, and any descriptor defined at class
creation time.

Your Summary, Revised


Concept Corrected Understanding
__new__ Allocates and returns instance; for immutables, establishes
final value
__init__ Initializes mutable state on an already-created instance;
cannot prevent creation

Subclass Inherits by default; defining one shadows parent’s unless


__init__ super() is explicit

Instance attribute Impossible at class definition time; use abstract properties


enforcement or template-method __init__
__init_subclas Runs at class definition, not instantiation; use for
s__ registration, validation, and declarative configuration;

Generated by [Link]
Concept Corrected Understanding

always forward kwargs


Your grasp of the distinction between class-definition-time and runtime behavior
is particularly solid—that is the conceptual hinge on which most of these
mechanisms turn.

User:
new and init __new__ allocates memory and returns an instance. __init__
expects self as argument, which mean that at the time it is called, an instance
already exists. It then fills it with attributes and properties.
inheritance and method shadowing Subclasses inherent methods from their
parent class and won’t override unless explicitly defined. They still can call
super().method.
Instance and Class attributes and properties For ensuring that a subclasses
has instance attributes, define them with @property and @abstractmethod
decorators. A distinction must be made: @property is a getter (it does not shows
up in __dict__), instead it access an attribute and can perform some validation
on the underlying attribute.
Class attributes must be defined outside __init__. If we instantiate an object of
that class, it can be accessible through dot notation. Additionally, they can
override/overshadowed via instance.cls_attr = value.
In order to ensure that subclasses possess certain class attributes,
__init_subclass__ should be utilized in the parent class. It is not a runtime
function, but rather a definition time function. The signature is
__init_subclass__(subclass, ...), where subclass stands for the
subclass type.
class Base:
def __init_subclass_(subclass):
subclass._foo = "foo"

def Concrete(Base):
pass

Generated by [Link]
>>> Concrete._foo
>>> "foo"

__init_subclass__ is useful for class-level variables and behavior. Can be


employed to define custom class variables (ensuring consistency across classes)
or for registration.

Kimi:
Your notes capture the core mechanics well. A few minor corrections and one
conceptual precision:

Code Example Typos


Your snippet has three small but important syntax issues:
class Base:
def __init_subclass_(subclass): # ❌ Missing trailing
__
subclass._foo = "foo" # ✅ Correct semantics,
but...

class Concrete(Base): # ❌ You wrote `def`


instead of `class`
pass

>>> Concrete._foo
>>> "foo"

Corrected:
class Base:
def __init_subclass__(cls, **kwargs): # ✅ Double
underscores; conventionally `cls`
cls._foo = "foo"
super().__init_subclass__(**kwargs) # ✅ Always forward
for MRO safety

class Concrete(Base):
pass

>>> Concrete._foo
'foo'

Generated by [Link]
One Conceptual Precision

“For ensuring that a subclasses has instance attributes, define them


with @property and @abstractmethod decorators.”

This is almost right, but worth sharpening: @abstractmethod + @property


enforces that the subclass implements a property getter on the class. It does
not literally guarantee that an attribute exists in the instance’s __dict__. The
subclass could compute the value on the fly without storing it:
class Base(ABC):
@property
@abstractmethod
def value(self): ...

class Child(Base):
@property
def value(self):
return 42 # No instance attribute stored anywhere!

If your goal is to guarantee that [Link] is actually stored as instance state,


you need a template-method __init__ instead:
class Base(ABC):
def __init__(self):
[Link] = self._get_value() # Enforces instance-level
storage

@abstractmethod
def _get_value(self): ...

Summary of Your Notes


Point Verdict

__new__ allocates; __init__ ✅ Correct


receives existing self

Subclasses inherit unless they ✅ Correct


override; super() available

@property is a descriptor, not in ✅ Correct


__dict__

Class attributes live outside ✅ Correct

Generated by [Link]
Point Verdict

__init__, shadowable per-


instance

__init_subclass__ is ✅ Correct
definition-time, not runtime

__init_subclass__ useful for ✅ Correct


registration and class-level
consistency
Overall, your mental model is solid. The only thing to internalize is the always-
forward-kwargs rule in __init_subclass__—it is the most common production
bug when people start using this feature with multiple inheritance or framework
hooks.

Generated by [Link]

You might also like