Python notes

dunder_methods

Overview of Dunder Methods in Python

  • Dunder methods allow Python class instances to interact with built-in functions and operators.
  • The term “dunder” comes from “double underscore”, as in __str__, __add__, etc.
  • These methods are often called implicitly by the language, not manually by the programmer.
  • Also known as "magic methods", though they behave like regular methods under the hood.

Basic Example of Operator Overloading

1 + 2.3           # 3.3
[1, 2] + [3, 4]   # [1, 2, 3, 4]
  • Python translates a + b into a.__add__(b) behind the scenes.
  • You can define your own behavior for operators like +, in, ==, etc., via dunder methods.

Commonly Used Dunder Method Example: __init__

class Square:
    def __init__(self, side_length):
        print("Inside init!")
        self.side_length = side_length

sq = Square(1)
# Output: Inside init!
  • __init__ sets up attributes when an object is instantiated.
  • Called implicitly by Python, not manually.

Operator Overloading Using Dunder Methods

  • Operators like +, in, ==, and others are backed by specific dunder methods.

    my_list = [2, 4, 6]
    3 in my_list              # False
    my_list.__contains__(3)   # False
  • You can override these behaviors in custom classes by defining the corresponding dunder methods.


Example: Using __missing__ in Dict Subclasses

class DictSubclass(dict):
    def __missing__(self, key):
        print(f"Missing {key = }")

my_dict = DictSubclass()
my_dict[0] = True

if my_dict[0]:
    print("Key 0 was `True`.")
my_dict[1]
# Output:
# Key 0 was `True`.
# Missing key = 1
  • __missing__ is triggered when a missing key is accessed in a dict subclass.

Practical Use: Reimplementing defaultdict

class my_defaultdict(dict):
    def __init__(self, default_factory, **kwargs):
        super().__init__(**kwargs)
        self.default_factory = default_factory

    def __missing__(self, key):
        self[key] = self.default_factory()
        return self[key]

olympic_medals = my_defaultdict(lambda: 0)
olympic_medals["Phelps"] = 28

print(olympic_medals["Phelps"])  # 28
print(olympic_medals["me"])      # 0
  • This mimics collections.defaultdict, returning and storing a default value when keys are missing.

Dunder Methods Reference Tables

Object Lifecycle, Representation & Attribute Access

Dunder method Usage / Needed for
__init__ Initialise object
__new__ Create object
__del__ Destroy object
__repr__ repr(obj) – official string representation
__str__ str(obj) or print(obj) – user-friendly string
__getattr__ Fallback for attribute access
__getattribute__ Standard attribute access (obj.name)
__setattr__ Set attribute values
__delattr__ Delete attribute
__dir__ dir(obj) listing

Comparison & Boolean Context

Dunder method Usage / Needed for
__eq__, __ne__ ==, !=
__lt__, __le__ <, <=
__gt__, __ge__ >, >=
__bool__ bool(obj) – define truthiness
__hash__ hash(obj) – hashable objects, dict keys

Item Access & Container Emulation

Dunder method Usage / Needed for
__getitem__ obj[key]
__setitem__ obj[key] = val
__delitem__ del obj[key]
__missing__ Missing key handler in dict subclasses
__contains__ val in obj
__iter__, __reversed__ Iteration: for, reversed()
__len__ len(obj)

Arithmetic Operators

Dunder method Usage / Needed for
__add__, __radd__, __iadd__ +, reverse +, in-place +=
__sub__, __rsub__, __isub__ -, reverse -, in-place -=
__mul__, __rmul__, __imul__ *, reverse *, in-place *=
__truediv__, __floordiv__ /, //
__mod__ %
__pow__ **

Bitwise & Numeric Conversions

Dunder method Usage / Needed for
__lshift__, __rshift__ <<, >>
__and__, __or__, __xor__ &, |, ^
__neg__, __pos__, __abs__ Unary -, +, abs()
__int__, __float__, __complex__ Type conversions
__index__ Integer conversion for slicing

Context Managers, Async & Callables

Dunder method Usage / Needed for
__call__ obj() – emulate callable objects
__enter__, __exit__ Context management (with)
__await__ Awaitable support
__aiter__, __anext__ Async iteration
__aenter__, __aexit__ Async context managers (async with)