Item 24: Use @classmethod polymorphism to construct object generically
Item 25: Use `super(__class__, self).__init__()` when inheriting
Item 29: use plain attributes instead of getter and setters.
Shortingcoming of @property: can only be shared by subclasses. Use descriptor to enable reusable property logic.
Item 31: descriptor
Need to use `WeakKeyDictionary` in `weakref` to keep track of attributes of different instances. A better solution is in Item 35.
Item 32: __getattr__, __getattribute__, __setattr__
__getattr__ is called if attribute cannot be found in instance dictionary.
__getattribute__ is called everytime even when attribute can be found in instance dictionary.
Avoid infinite recursion in __getattribute__ by using methods from `super()`
Item 33: validate subclasses with metaclass
Validate code in __init__ is runned after an object of the class's type is constructed. Using metaclasses can raise errors much earlier.
Item 34: register class existence with metaclass
Class registration is a helpful pattern for building modualr python programs. Using mataclasses for class registration avoids errors by ensuring that you never miss a registration call.
Item 35: annotate class attributes with metaclass
In this way you can avoid both memory leaks and `weakref` module by metaclasses along with descriptors.