[{"data":1,"prerenderedAt":85},["ShallowReactive",2],{"qa-\u002Fpython\u002Foop\u002Fdunder-methods":3},{"page":4,"siblings":61,"blog":82},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"related":52,"seo":53,"seoDescription":54,"stem":55,"subtopic":56,"topic":57,"topicSlug":58,"updated":59,"__hash__":60},"qa\u002Fpython\u002Foop\u002Fdunder-methods.md","Dunder Methods",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"hard","md","Python","python",{},true,3,"\u002Fpython\u002Foop\u002Fdunder-methods",[23,28,32,36,40,44,48],{"id":24,"difficulty":25,"q":26,"a":27},"what-are-dunder-methods","medium","What are dunder (magic) methods?","**Dunder methods** (\"double underscore\", e.g. `__init__`, `__len__`) are special\nhooks Python calls **implicitly** to make your objects work with built-in syntax\nand functions. They are how Python implements **operator overloading and\nprotocols** — `len(x)` calls `x.__len__()`, `x + y` calls `x.__add__(y)`,\n`x[0]` calls `x.__getitem__(0)`.\n\n```python\nclass Box:\n    def __init__(self, items):\n        self.items = items\n    def __len__(self):\n        return len(self.items)   # makes len(box) work\n\nb = Box([1, 2, 3])\nlen(b)        # 3 — Python calls b.__len__()\n```\n\nThey're sometimes called \"magic methods\" but there's no magic — they're a\ndocumented protocol. Implementing the right dunders makes your objects feel like\nnative Python types.\n",{"id":29,"difficulty":25,"q":30,"a":31},"repr-str-dunder","How do __repr__ and __str__ control how an object prints?","`__repr__` defines the **unambiguous developer representation** (REPL output,\ncontainers, `repr()`); `__str__` defines the **human-readable** form (`print()`,\n`str()`). When `__str__` is absent, Python **falls back to `__repr__`** — so\n`__repr__` is the one to always implement.\n\n```python\nclass Temperature:\n    def __init__(self, c):\n        self.c = c\n    def __repr__(self):\n        return f\"Temperature({self.c})\"     # eval-friendly\n    def __str__(self):\n        return f\"{self.c}°C\"                 # friendly\n\nt = Temperature(20)\nstr(t)    # '20°C'\nrepr(t)   # 'Temperature(20)'\n```\n\nA good `__repr__` ideally lets `eval(repr(obj))` reconstruct the object. Always\ndefine `__repr__`; add `__str__` only when a separate user-facing string helps.\n",{"id":33,"difficulty":14,"q":34,"a":35},"eq-and-hash","How do __eq__ and __hash__ work together?","`__eq__` defines **value equality** (`==`); `__hash__` returns the integer used\nby **dicts and sets**. They have a **contract**: if `a == b` then\n`hash(a) == hash(b)`. Defining `__eq__` **alone** sets `__hash__ = None`, making\ninstances **unhashable** — so define both, derived from the **same immutable\nfields**.\n\n```python\nclass Point:\n    def __init__(self, x, y):\n        self.x, self.y = x, y\n    def __eq__(self, other):\n        return (self.x, self.y) == (other.x, other.y)\n    def __hash__(self):\n        return hash((self.x, self.y))    # same fields as __eq__\n\n{Point(1, 2), Point(1, 2)}   # one element — treated as equal\n```\n\nOnly hash on fields that never change after creation. If you break the contract,\nobjects you store in a set\u002Fdict become unfindable.\n",{"id":37,"difficulty":14,"q":38,"a":39},"operator-overloading","How do you overload operators like + and *?","Implement the matching **arithmetic dunder**: `__add__` for `+`, `__sub__` for\n`-`, `__mul__` for `*`, `__lt__` for `\u003C`, and so on. Python calls them when the\noperator is used. Return a **new object** (or `NotImplemented` if you can't\nhandle the other operand, so Python can try the reflected method like\n`__radd__`).\n\n```python\nclass Vector:\n    def __init__(self, x, y):\n        self.x, self.y = x, y\n    def __add__(self, other):\n        return Vector(self.x + other.x, self.y + other.y)   # v1 + v2\n    def __mul__(self, k):\n        return Vector(self.x * k, self.y * k)               # v * 3\n    def __repr__(self):\n        return f\"Vector({self.x}, {self.y})\"\n\nVector(1, 2) + Vector(3, 4)   # Vector(4, 6)\nVector(1, 2) * 3              # Vector(3, 6)\n```\n\nReturn `NotImplemented` (not raise) for unsupported types so Python can fall\nback gracefully. Don't overload operators in surprising ways — keep semantics\nintuitive.\n",{"id":41,"difficulty":14,"q":42,"a":43},"sequence-protocol","How do __len__ and __getitem__ make an object behave like a sequence?","Implementing `__len__` makes `len(obj)` work, and `__getitem__` makes\n**indexing, slicing, and iteration** work — Python can iterate by calling\n`__getitem__(0)`, `__getitem__(1)`, ... until `IndexError`, even without an\n`__iter__`. Together they form the **sequence protocol**.\n\n```python\nclass Playlist:\n    def __init__(self, songs):\n        self.songs = songs\n    def __len__(self):\n        return len(self.songs)         # len(pl)\n    def __getitem__(self, i):\n        return self.songs[i]           # pl[0], pl[1:3], and iteration\n\npl = Playlist([\"a\", \"b\", \"c\"])\nlen(pl)          # 3\npl[1]            # 'b'\nfor s in pl:     # iterates via __getitem__\n    print(s)\n```\n\nAdd `__contains__` for `in` and `__setitem__` for assignment. This duck-typed\nprotocol is why custom containers feel like lists.\n",{"id":45,"difficulty":25,"q":46,"a":47},"call-dunder","What does __call__ do?","`__call__` makes an **instance itself callable** like a function — `obj()`\ninvokes `obj.__call__()`. This lets objects **carry state between calls**, which\na plain function can't do as cleanly. It's the basis of function objects and\nmany decorators.\n\n```python\nclass Multiplier:\n    def __init__(self, factor):\n        self.factor = factor       # remembered state\n    def __call__(self, x):\n        return x * self.factor     # obj(x)\n\ndouble = Multiplier(2)\ndouble(5)         # 10 — calling the instance\ncallable(double)  # True\n```\n\nUse it for **stateful callables** — configurable functions, accumulators, or\nclass-based decorators. If you just need behavior without state, a closure or\nplain function is simpler.\n",{"id":49,"difficulty":25,"q":50,"a":51},"total-ordering","What does functools.total_ordering do?","`@functools.total_ordering` is a class decorator that **fills in the missing\ncomparison operators** from the ones you define. You provide `__eq__` plus **one**\nof `__lt__`, `__le__`, `__gt__`, `__ge__`, and it generates the rest — saving you\nfrom writing all six.\n\n```python\nfrom functools import total_ordering\n\n@total_ordering\nclass Version:\n    def __init__(self, n):\n        self.n = n\n    def __eq__(self, other):\n        return self.n == other.n\n    def __lt__(self, other):\n        return self.n \u003C other.n     # only this + __eq__ needed\n\nVersion(1) \u003C Version(2)    # True\nVersion(2) >= Version(1)   # True — generated by total_ordering\nsorted([Version(3), Version(1), Version(2)])  # works\n```\n\nThe trade-off is a small performance cost from derived comparisons; for\nhot-path code, define all operators explicitly. Otherwise it's a clean way to\nget full ordering with minimal boilerplate.\n",null,{"description":11},"Python interview questions on dunder\u002Fmagic methods — __repr__\u002F__str__, __eq__ and __hash__, operator overloading with __add__, the sequence protocol (__len__\u002F__getitem__), __call__, and functools.total_ordering.","python\u002Foop\u002Fdunder-methods","Dunder \u002F Magic Methods","Object-Oriented Programming","oop","2026-06-18","ncqznv9sUn2_SLebvm5cJygSM3_p-P2icZsoSaPIlxo",[62,66,69,70,74,78],{"subtopic":63,"path":64,"order":65},"Inheritance & the MRO","\u002Fpython\u002Foop\u002Finheritance",1,{"subtopic":67,"path":68,"order":12},"Classes, Instances & __init__","\u002Fpython\u002Foop\u002Fclasses",{"subtopic":56,"path":21,"order":20},{"subtopic":71,"path":72,"order":73},"Methods & Properties","\u002Fpython\u002Foop\u002Fmethods-properties",4,{"subtopic":75,"path":76,"order":77},"Dataclasses & __slots__","\u002Fpython\u002Foop\u002Fdataclasses-slots",5,{"subtopic":79,"path":80,"order":81},"Abstract Base Classes & Protocols","\u002Fpython\u002Foop\u002Fabc-protocols",6,{"path":83,"title":84},"\u002Fblog\u002Fpython-dunder-magic-methods-explained","Python Dunder Methods Explained — Operator Overloading and the Data Model",1781808676622]