Notice how super() calls the next class in MRO, not necessarily the parent. This is cooperative multiple inheritance.
Rule: Always use super() in inheritance hierarchies, even for single inheritance. It’s future-proof.
Descriptors are the secret sauce behind properties, methods, and class methods. A descriptor is an object that defines one of __get__, __set__, or __delete__.
When you define a property using @property, you are actually creating a descriptor instance in the class. python 3 deep dive part 4 oop high quality
How it works:
If a class attribute defines __get__, accessing obj.attr becomes AttributeClass.__get__(attr, obj, type(obj)).
Example: A Validator Descriptor
class ValidString:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, obj, owner):
return obj.__dict__.get(self.name)
def __set__(self, obj, value):
if not isinstance(value, str):
raise TypeError(f"self.name must be a string")
obj.__dict__[self.name] = value
class Person:
name = ValidString() # Using the descriptor
p = Person()
p.name = "Alice" # Works
# p.name = 123 # Raises TypeError
This is how Django models and SQLAlchemy columns work under the hood.
By default, == checks for value equality (calling __eq__), while is checks for identity (memory address via id()). Notice how super() calls the next class in
a = [1, 2]
b = [1, 2]
print(a == b) # True (Values are equal)
print(a is b) # False (Different objects in memory)
In Python, classes are objects too. Just as instances are created by classes, classes are created by metaclasses. The default metaclass is type.
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
super() is not just "call parent method". In Python, super() delegates to the next class in the Method Resolution Order (MRO).
Common mistake:
class A:
def show(self):
print("A")
class B(A):
def show(self):
print("B")
super().show() # Works, but rigid
Real power: Cooperative multiple inheritance: Descriptors are the secret sauce behind properties, methods,
class Base:
def process(self):
print("Base")
class LogMixin:
def process(self):
print("Logging start")
super().process()
print("Logging end")
class ValidateMixin:
def process(self):
print("Validating")
super().process()
class Concrete(LogMixin, ValidateMixin, Base):
pass
c = Concrete()
c.process()