Most classes in Python should have been functions. The test is not “is this a noun” — it is whether there is invalid state a class can prevent.
self is a parameter name, not a keyword. __init__ does not create the object. And a class attribute is shared until the moment you assign through an instance.
A class without __repr__ wastes your time from the moment it exists. repr is for you, str is for the reader, and one of them has a sensible fallback.
Python has no need for getters and setters, because @property lets you add validation later without changing a single caller.
The question is only ever what the method needs — this instance, the class, or neither. Getting it wrong quietly breaks subclasses.
Inheritance gives a subclass everything, including what it should not have. The penguin problem, and what to do instead.
Every operator and built-in in Python is asking your object a question. Answer it and your class behaves like a native type.
Four ways to hold structured data in Python. The deciding question is not style — it is whether a typo should fail loudly.
asdict() converts a dataclass to a dict and recurses the whole way down, copying as it goes. That is usually what you want, and occasionally very much not.
Duck typing fails at the call site, long after the mistake. ABCs and Protocols both fix that, in opposite directions.
__slots__ removes the per-instance dict. It saves real memory at scale, catches typos for free, and is the wrong default.
Every operator, every built-in, every piece of syntax in Python is a question asked of your object. The data model is the list of questions.