8.1. Metaprogramming Init Subclass

  • Created for the people, to reduce times when you need metaclasses

class Account:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)

8.1.1. Example

class Account:
    def __init_subclass__(cls, **kwargs):
        print('Account init_subclass')

class User(Account):
   pass

Account init_subclass

8.1.2. Arguments

class Account:
    def __init_subclass__(cls, debug=False, **kwargs):
        print(f'Account init_subclass: {debug=}')

Creating class without specifying the argument will use the default value:

class User(Account):
   pass

Account init_subclass: debug=False

Now, let's pass the argument to the subclass:

class User(Account, debug=True):
   pass

Account init_subclass: debug=True

8.1.3. Use Case - 1

from uuid import uuid4
from datetime import datetime, timezone
from pprint import pprint
class Account:
    def __init_subclass__(cls, debug=False, **kwargs):
        if debug:
            cls._uuid = uuid4()
            cls._since = datetime.now(timezone.utc)

class User(Account):
    pass

pprint(vars(User))
mappingproxy({'__doc__': None,
              '__firstlineno__': 1,
              '__module__': '__main__',
              '__static_attributes__': ()})
class User(Account, debug=True):
    pass

pprint(vars(User))
mappingproxy({'__doc__': None,
              '__module__': '__main__',
              '_since': datetime.datetime(2024, 8, 20, 14, 55, 59, 764071, tzinfo=datetime.timezone.utc),
              '_uuid': UUID('6d1bc92c-ddb6-47cb-a872-24f94f3d1be4')})

8.1.4. Use Case - 2

import string

string.ascii_uppercase
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

tuple(string.ascii_uppercase)
('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z')
class Account:
    def __init_subclass__(cls, strict=False, **kwargs):
        if strict:
            uppercase = tuple(string.ascii_uppercase)
            assert cls.__name__.startswith(uppercase), \
            'Class name must start with uppercase letter'
        super().__init_subclass__()
class user(Account):
    pass
class user(Account, strict=True):
    pass

Traceback (most recent call last):
AssertionError: Class name must start with uppercase letter
class User(Account):
    pass
class User(Account, strict=True):
    pass

8.1.5. Use Case - 3

from pprint import pprint


class PositionMixin:
    def __init_subclass__(cls, **kwargs):
        cls.position_x = 0
        cls.position_y = 0
        super().__init_subclass__(**kwargs)


class HealthMixin:
    def __init_subclass__(cls, **kwargs):
        cls.health = 100
        super().__init_subclass__(**kwargs)


class Hero(PositionMixin, HealthMixin):
    def __init__(self, name):
        self.name = name
hero = Hero('Twardowski')

hero.position_x
0
hero.position_y
0
hero.health
100
vars(hero)
{'name': 'Twardowski'}
pprint(vars(Hero))
mappingproxy({'__doc__': None,
              '__firstlineno__': 1,
              '__init__': <function Hero.__init__ at 0x...>,
              '__module__': '__main__',
              '__static_attributes__': ('name',),
              'health': 100,
              'position_x': 0,
              'position_y': 0})