7.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)
7.1.1. Example
>>> class Account:
... def __init_subclass__(cls, **kwargs):
... print('Account init_subclass')
...
>>> class User(Account):
... pass
...
Account init_subclass
7.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
7.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')})
7.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
7.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})