Skip to content

Chapter 5 of 8

Object-Oriented Programming and Dunder Methods

Python integrates objects with language syntax through dunder (double underscore) methods. `__init__` initializes a newly created instance, while `__new__` actually creates and returns it. `__repr__` returns an unambiguous string useful for debugging, while `__str__` returns a human-readable version. `__eq__` defines equality, and `__hash__` returns a hash value required for objects used as dictionary keys or set members. `__call__` makes instances callable like functions. Attribute access hooks include `__getattr__` (called when normal lookup fails) and `__getattribute__` (called for every access — powerful but easy to misuse). `__setattr__` runs when setting an attribute, `__getitem__` implements `obj[key]` indexing, `__iter__` returns an iterator, and `__enter__`/`__exit__` implement the context manager protocol used by the `with` statement for resource management. The `contextlib` module and `@contextmanager` decorator provide convenient ways to build context managers.

Class structure distinguishes class attributes (shared across all instances) from instance attributes (per-instance). `__slots__` restricts which instance attributes may be set, saving memory for high-volume classes. Multiple inheritance is supported, and Python resolves method lookup using the C3 linearization Method Resolution Order (MRO); `super()` returns a proxy for invoking parent methods. Abstract Base Classes cannot be instantiated directly and may declare methods marked with `@abstractmethod` that subclasses must implement. Structural subtyping via Protocol classes lets a class qualify by having required methods, in contrast to nominal subtyping, which checks declared inheritance. `isinstance()` checks whether an object is an instance of a class or its subclass, and `issubclass()` checks class-to-class relationships. `@classmethod` receives the class as `cls`, while `@staticmethod` receives neither class nor instance.

More advanced features include the descriptor protocol: objects implementing `__get__`, `__set__`, or `__delete__` control attribute access. `@property` makes a method accessible like an attribute, while `@cached_property` computes the value once and caches it. The `__set_name__` descriptor hook lets a descriptor learn the attribute name it's bound to. Metaclasses are classes whose instances are classes, controlling class creation; `type()` is the default metaclass and can also create classes dynamically. `__init_subclass__` runs when a subclass is created, enabling hooks. Dataclasses, declared with the `@dataclass` decorator, auto-generate `__init__`, `__repr__`, and equality methods from annotations. `dataclass(frozen=True)` makes instances immutable, `dataclass(slots=True)` generates `__slots__`, and `__post_init__` runs additional initialization after the generated `__init__`.

All chapters
  1. 1Python Foundations and Principles
  2. 2Core Data Types and Type Concepts
  3. 3Iteration, Generators, and Comprehensions
  4. 4Functions, Decorators, and Modules
  5. 5Object-Oriented Programming and Dunder Methods
  6. 6Type Hinting and Modern Language Features
  7. 7Concurrency: GIL, Threads, Processes, and Async
  8. 8Standard Library, Tooling, and Ecosystem

Drill it

Reading is not remembering. These come from the Python Deep Dive deck:

Q

What is Python?

A high-level, interpreted, dynamically typed programming language created by Guido van Rossum in 1991.

Q

What is PEP 8?

Python's official style guide.

Q

What is PEP 20?

The Zen of Python — guiding principles by Tim Peters.

Q

What is the difference between Python 2 and Python 3?

Python 3 is the actively maintained version; Python 2 reached end-of-life in 2020.