[{"data":1,"prerenderedAt":1274},["ShallowReactive",2],{"blog-\u002Fblog\u002Fpython-dunder-magic-methods-explained":3},{"id":4,"title":5,"body":6,"description":1260,"difficulty":1261,"extension":1262,"framework":1263,"frameworkSlug":85,"meta":1264,"navigation":189,"order":93,"path":1265,"qaPath":1266,"seo":1267,"stem":1268,"subtopic":1269,"topic":1270,"topicSlug":1271,"updated":1272,"__hash__":1273},"blog\u002Fblog\u002Fpython-dunder-magic-methods-explained.md","Python Dunder Methods Explained — Operator Overloading and the Data Model",{"type":7,"value":8,"toc":1246},"minimark",[9,14,50,54,80,226,233,243,275,441,452,463,495,604,607,611,634,789,799,803,830,981,987,994,1001,1088,1092,1109,1198,1202,1242],[10,11,13],"h2",{"id":12},"python-dunder-methods-explained","Python dunder methods, explained",[15,16,17,18,22,23,22,26,22,29,32,33,36,37,41,42,45,46,49],"p",{},"The reason ",[19,20,21],"code",{},"len(x)",", ",[19,24,25],{},"x + y",[19,27,28],{},"x[0]",[19,30,31],{},"for i in x",", and ",[19,34,35],{},"print(x)"," all \"just work\" on your\nown classes is Python's ",[38,39,40],"strong",{},"data model",": a set of specially named ",[38,43,44],{},"dunder"," methods\n(double-underscore, like ",[19,47,48],{},"__len__",") that the interpreter calls behind the scenes. Master\nthese and your objects integrate seamlessly with the language's syntax. This guide covers\nthe dunders interviewers ask about most.",[10,51,53],{"id":52},"what-dunder-methods-are","What dunder methods are",[15,55,56,57,60,61,63,64,22,67,63,69,22,72,75,76,79],{},"Dunder (\"double underscore\") methods — also called ",[38,58,59],{},"magic methods"," — are hooks Python\ncalls for built-in operations. You rarely call them directly; you trigger them through\nsyntax. ",[19,62,21],{}," calls ",[19,65,66],{},"x.__len__()",[19,68,25],{},[19,70,71],{},"x.__add__(y)",[19,73,74],{},"x[k]"," calls\n",[19,77,78],{},"x.__getitem__(k)",".",[81,82,87],"pre",{"className":83,"code":84,"language":85,"meta":86,"style":86},"language-python shiki shiki-themes github-light github-dark","class Vector:\n    def __init__(self, x, y):\n        self.x, self.y = x, y\n    def __add__(self, other):           # called by the + operator\n        return Vector(self.x + other.x, self.y + other.y)\n\nVector(1, 2) + Vector(3, 4)             # Python calls __add__ for you\n","python","",[19,88,89,106,119,140,155,184,191],{"__ignoreMap":86},[90,91,94,98,102],"span",{"class":92,"line":93},"line",1,[90,95,97],{"class":96},"szBVR","class",[90,99,101],{"class":100},"sScJk"," Vector",[90,103,105],{"class":104},"sVt8B",":\n",[90,107,109,112,116],{"class":92,"line":108},2,[90,110,111],{"class":96},"    def",[90,113,115],{"class":114},"sj4cs"," __init__",[90,117,118],{"class":104},"(self, x, y):\n",[90,120,122,125,128,131,134,137],{"class":92,"line":121},3,[90,123,124],{"class":114},"        self",[90,126,127],{"class":104},".x, ",[90,129,130],{"class":114},"self",[90,132,133],{"class":104},".y ",[90,135,136],{"class":96},"=",[90,138,139],{"class":104}," x, y\n",[90,141,143,145,148,151],{"class":92,"line":142},4,[90,144,111],{"class":96},[90,146,147],{"class":114}," __add__",[90,149,150],{"class":104},"(self, other):           ",[90,152,154],{"class":153},"sJ8bj","# called by the + operator\n",[90,156,158,161,164,166,169,172,175,177,179,181],{"class":92,"line":157},5,[90,159,160],{"class":96},"        return",[90,162,163],{"class":104}," Vector(",[90,165,130],{"class":114},[90,167,168],{"class":104},".x ",[90,170,171],{"class":96},"+",[90,173,174],{"class":104}," other.x, ",[90,176,130],{"class":114},[90,178,133],{"class":104},[90,180,171],{"class":96},[90,182,183],{"class":104}," other.y)\n",[90,185,187],{"class":92,"line":186},6,[90,188,190],{"emptyLinePlaceholder":189},true,"\n",[90,192,194,197,200,202,205,208,210,212,215,217,220,223],{"class":92,"line":193},7,[90,195,196],{"class":104},"Vector(",[90,198,199],{"class":114},"1",[90,201,22],{"class":104},[90,203,204],{"class":114},"2",[90,206,207],{"class":104},") ",[90,209,171],{"class":96},[90,211,163],{"class":104},[90,213,214],{"class":114},"3",[90,216,22],{"class":104},[90,218,219],{"class":114},"4",[90,221,222],{"class":104},")             ",[90,224,225],{"class":153},"# Python calls __add__ for you\n",[15,227,228,229,232],{},"The idea is ",[38,230,231],{},"protocols, not inheritance",": anything that implements the right dunders\nbehaves like a built-in, no base class required.",[10,234,236,239,240],{"id":235},"repr-vs-str",[38,237,238],{},"repr"," vs ",[38,241,242],{},"str",[15,244,245,246,249,250,253,254,249,257,260,261,264,265,268,269,271,272,274],{},"Both produce a string, but for different audiences. ",[19,247,248],{},"__repr__"," is the ",[38,251,252],{},"unambiguous,\ndeveloper-facing"," representation (ideally something that could recreate the object);\n",[19,255,256],{},"__str__",[38,258,259],{},"readable, user-facing"," one. ",[19,262,263],{},"str()","\u002F",[19,266,267],{},"print"," use ",[19,270,256],{}," and fall back\nto ",[19,273,248],{}," if it's missing.",[81,276,278],{"className":83,"code":277,"language":85,"meta":86,"style":86},"class Point:\n    def __init__(self, x, y):\n        self.x, self.y = x, y\n    def __repr__(self):\n        return f\"Point(x={self.x}, y={self.y})\"   # for developers \u002F the REPL\n    def __str__(self):\n        return f\"({self.x}, {self.y})\"            # for end users\n\np = Point(1, 2)\nrepr(p)   # 'Point(x=1, y=2)'\nstr(p)    # '(1, 2)'\n",[19,279,280,289,297,311,321,357,366,394,399,419,430],{"__ignoreMap":86},[90,281,282,284,287],{"class":92,"line":93},[90,283,97],{"class":96},[90,285,286],{"class":100}," Point",[90,288,105],{"class":104},[90,290,291,293,295],{"class":92,"line":108},[90,292,111],{"class":96},[90,294,115],{"class":114},[90,296,118],{"class":104},[90,298,299,301,303,305,307,309],{"class":92,"line":121},[90,300,124],{"class":114},[90,302,127],{"class":104},[90,304,130],{"class":114},[90,306,133],{"class":104},[90,308,136],{"class":96},[90,310,139],{"class":104},[90,312,313,315,318],{"class":92,"line":142},[90,314,111],{"class":96},[90,316,317],{"class":114}," __repr__",[90,319,320],{"class":104},"(self):\n",[90,322,323,325,328,332,335,338,341,344,346,349,351,354],{"class":92,"line":157},[90,324,160],{"class":96},[90,326,327],{"class":96}," f",[90,329,331],{"class":330},"sZZnC","\"Point(x=",[90,333,334],{"class":114},"{self",[90,336,337],{"class":104},".x",[90,339,340],{"class":114},"}",[90,342,343],{"class":330},", y=",[90,345,334],{"class":114},[90,347,348],{"class":104},".y",[90,350,340],{"class":114},[90,352,353],{"class":330},")\"",[90,355,356],{"class":153},"   # for developers \u002F the REPL\n",[90,358,359,361,364],{"class":92,"line":186},[90,360,111],{"class":96},[90,362,363],{"class":114}," __str__",[90,365,320],{"class":104},[90,367,368,370,372,375,377,379,381,383,385,387,389,391],{"class":92,"line":193},[90,369,160],{"class":96},[90,371,327],{"class":96},[90,373,374],{"class":330},"\"(",[90,376,334],{"class":114},[90,378,337],{"class":104},[90,380,340],{"class":114},[90,382,22],{"class":330},[90,384,334],{"class":114},[90,386,348],{"class":104},[90,388,340],{"class":114},[90,390,353],{"class":330},[90,392,393],{"class":153},"            # for end users\n",[90,395,397],{"class":92,"line":396},8,[90,398,190],{"emptyLinePlaceholder":189},[90,400,402,405,407,410,412,414,416],{"class":92,"line":401},9,[90,403,404],{"class":104},"p ",[90,406,136],{"class":96},[90,408,409],{"class":104}," Point(",[90,411,199],{"class":114},[90,413,22],{"class":104},[90,415,204],{"class":114},[90,417,418],{"class":104},")\n",[90,420,422,424,427],{"class":92,"line":421},10,[90,423,238],{"class":114},[90,425,426],{"class":104},"(p)   ",[90,428,429],{"class":153},"# 'Point(x=1, y=2)'\n",[90,431,433,435,438],{"class":92,"line":432},11,[90,434,242],{"class":114},[90,436,437],{"class":104},"(p)    ",[90,439,440],{"class":153},"# '(1, 2)'\n",[15,442,443,448,449,451],{},[38,444,445,446],{},"Always define ",[19,447,248],{}," — it's what you see in the debugger, in logs, and inside\ncontainers. ",[19,450,256],{}," is optional and only worth adding when the user-facing form differs.",[10,453,455,458,459,462],{"id":454},"eq-and-hash-go-together",[38,456,457],{},"eq"," and ",[38,460,461],{},"hash"," go together",[15,464,465,468,469,472,473,476,477,480,481,483,484,487,488,490,491,494],{},[19,466,467],{},"__eq__"," defines value equality (",[19,470,471],{},"==","); ",[19,474,475],{},"__hash__"," lets instances live in sets and act as\ndict keys. They must be ",[38,478,479],{},"consistent",": equal objects must have equal hashes. Defining\n",[19,482,467],{}," alone makes the class ",[38,485,486],{},"unhashable"," (Python sets ",[19,489,475],{}," to ",[19,492,493],{},"None",").",[81,496,498],{"className":83,"code":497,"language":85,"meta":86,"style":86},"class Money:\n    def __init__(self, cents):\n        self.cents = cents\n    def __eq__(self, other):\n        return self.cents == other.cents\n    def __hash__(self):\n        return hash(self.cents)        # consistent with __eq__\n\n{Money(100), Money(100)}               # one element — treated as equal\n",[19,499,500,509,518,530,540,554,563,581,585],{"__ignoreMap":86},[90,501,502,504,507],{"class":92,"line":93},[90,503,97],{"class":96},[90,505,506],{"class":100}," Money",[90,508,105],{"class":104},[90,510,511,513,515],{"class":92,"line":108},[90,512,111],{"class":96},[90,514,115],{"class":114},[90,516,517],{"class":104},"(self, cents):\n",[90,519,520,522,525,527],{"class":92,"line":121},[90,521,124],{"class":114},[90,523,524],{"class":104},".cents ",[90,526,136],{"class":96},[90,528,529],{"class":104}," cents\n",[90,531,532,534,537],{"class":92,"line":142},[90,533,111],{"class":96},[90,535,536],{"class":114}," __eq__",[90,538,539],{"class":104},"(self, other):\n",[90,541,542,544,547,549,551],{"class":92,"line":157},[90,543,160],{"class":96},[90,545,546],{"class":114}," self",[90,548,524],{"class":104},[90,550,471],{"class":96},[90,552,553],{"class":104}," other.cents\n",[90,555,556,558,561],{"class":92,"line":186},[90,557,111],{"class":96},[90,559,560],{"class":114}," __hash__",[90,562,320],{"class":104},[90,564,565,567,570,573,575,578],{"class":92,"line":193},[90,566,160],{"class":96},[90,568,569],{"class":114}," hash",[90,571,572],{"class":104},"(",[90,574,130],{"class":114},[90,576,577],{"class":104},".cents)        ",[90,579,580],{"class":153},"# consistent with __eq__\n",[90,582,583],{"class":92,"line":396},[90,584,190],{"emptyLinePlaceholder":189},[90,586,587,590,593,596,598,601],{"class":92,"line":401},[90,588,589],{"class":104},"{Money(",[90,591,592],{"class":114},"100",[90,594,595],{"class":104},"), Money(",[90,597,592],{"class":114},[90,599,600],{"class":104},")}               ",[90,602,603],{"class":153},"# one element — treated as equal\n",[15,605,606],{},"Hash only on fields that never change after creation; otherwise a mutated key gets lost in\nits hash bucket.",[10,608,610],{"id":609},"operator-overloading","Operator overloading",[15,612,613,614,616,617,22,620,616,623,22,626,629,630,633],{},"Arithmetic and comparison operators map to dunders: ",[19,615,171],{}," → ",[19,618,619],{},"__add__",[19,621,622],{},"*",[19,624,625],{},"__mul__",[19,627,628],{},"\u003C"," →\n",[19,631,632],{},"__lt__",", and so on. Implementing them lets your objects participate in expressions\nnaturally.",[81,635,637],{"className":83,"code":636,"language":85,"meta":86,"style":86},"class Money:\n    def __init__(self, cents): self.cents = cents\n    def __add__(self, other):  return Money(self.cents + other.cents)\n    def __lt__(self, other):   return self.cents \u003C other.cents\n    def __repr__(self):        return f\"${self.cents\u002F100:.2f}\"\n\nMoney(150) + Money(50)   # $2.00\nMoney(150) \u003C Money(200)  # True\n",[19,638,639,647,664,688,708,741,745,768],{"__ignoreMap":86},[90,640,641,643,645],{"class":92,"line":93},[90,642,97],{"class":96},[90,644,506],{"class":100},[90,646,105],{"class":104},[90,648,649,651,653,656,658,660,662],{"class":92,"line":108},[90,650,111],{"class":96},[90,652,115],{"class":114},[90,654,655],{"class":104},"(self, cents): ",[90,657,130],{"class":114},[90,659,524],{"class":104},[90,661,136],{"class":96},[90,663,529],{"class":104},[90,665,666,668,670,673,676,679,681,683,685],{"class":92,"line":121},[90,667,111],{"class":96},[90,669,147],{"class":114},[90,671,672],{"class":104},"(self, other):  ",[90,674,675],{"class":96},"return",[90,677,678],{"class":104}," Money(",[90,680,130],{"class":114},[90,682,524],{"class":104},[90,684,171],{"class":96},[90,686,687],{"class":104}," other.cents)\n",[90,689,690,692,695,698,700,702,704,706],{"class":92,"line":142},[90,691,111],{"class":96},[90,693,694],{"class":114}," __lt__",[90,696,697],{"class":104},"(self, other):   ",[90,699,675],{"class":96},[90,701,546],{"class":114},[90,703,524],{"class":104},[90,705,628],{"class":96},[90,707,553],{"class":104},[90,709,710,712,714,717,719,721,724,726,729,731,733,736,738],{"class":92,"line":157},[90,711,111],{"class":96},[90,713,317],{"class":114},[90,715,716],{"class":104},"(self):        ",[90,718,675],{"class":96},[90,720,327],{"class":96},[90,722,723],{"class":330},"\"$",[90,725,334],{"class":114},[90,727,728],{"class":104},".cents",[90,730,264],{"class":96},[90,732,592],{"class":114},[90,734,735],{"class":96},":.2f",[90,737,340],{"class":114},[90,739,740],{"class":330},"\"\n",[90,742,743],{"class":92,"line":186},[90,744,190],{"emptyLinePlaceholder":189},[90,746,747,750,753,755,757,759,762,765],{"class":92,"line":193},[90,748,749],{"class":104},"Money(",[90,751,752],{"class":114},"150",[90,754,207],{"class":104},[90,756,171],{"class":96},[90,758,678],{"class":104},[90,760,761],{"class":114},"50",[90,763,764],{"class":104},")   ",[90,766,767],{"class":153},"# $2.00\n",[90,769,770,772,774,776,778,780,783,786],{"class":92,"line":396},[90,771,749],{"class":104},[90,773,752],{"class":114},[90,775,207],{"class":104},[90,777,628],{"class":96},[90,779,678],{"class":104},[90,781,782],{"class":114},"200",[90,784,785],{"class":104},")  ",[90,787,788],{"class":153},"# True\n",[15,790,791,792,795,796,798],{},"For comparisons, ",[19,793,794],{},"functools.total_ordering"," fills in the rest from just ",[19,797,467],{}," and one\nordering method, saving boilerplate.",[10,800,802],{"id":801},"the-sequencecontainer-protocol","The sequence\u002Fcontainer protocol",[15,804,805,806,458,808,811,812,815,816,819,820,822,823,826,827,79],{},"Implement ",[19,807,48],{},[19,809,810],{},"__getitem__"," and your object behaves like a sequence — it supports\n",[19,813,814],{},"len()",", indexing, slicing, ",[38,817,818],{},"and iteration"," (Python falls back to ",[19,821,810],{}," with\n0, 1, 2… if there's no ",[19,824,825],{},"__iter__","), plus ",[19,828,829],{},"in",[81,831,833],{"className":83,"code":832,"language":85,"meta":86,"style":86},"class Deck:\n    def __init__(self, cards): self._cards = cards\n    def __len__(self):              return len(self._cards)\n    def __getitem__(self, i):       return self._cards[i]\n\ndeck = Deck([\"A\", \"K\", \"Q\"])\nlen(deck)        # 3\ndeck[0]          # 'A'\nfor c in deck:   # iterable for free\n    print(c)\n",[19,834,835,844,863,885,902,906,932,943,957,973],{"__ignoreMap":86},[90,836,837,839,842],{"class":92,"line":93},[90,838,97],{"class":96},[90,840,841],{"class":100}," Deck",[90,843,105],{"class":104},[90,845,846,848,850,853,855,858,860],{"class":92,"line":108},[90,847,111],{"class":96},[90,849,115],{"class":114},[90,851,852],{"class":104},"(self, cards): ",[90,854,130],{"class":114},[90,856,857],{"class":104},"._cards ",[90,859,136],{"class":96},[90,861,862],{"class":104}," cards\n",[90,864,865,867,870,873,875,878,880,882],{"class":92,"line":121},[90,866,111],{"class":96},[90,868,869],{"class":114}," __len__",[90,871,872],{"class":104},"(self):              ",[90,874,675],{"class":96},[90,876,877],{"class":114}," len",[90,879,572],{"class":104},[90,881,130],{"class":114},[90,883,884],{"class":104},"._cards)\n",[90,886,887,889,892,895,897,899],{"class":92,"line":142},[90,888,111],{"class":96},[90,890,891],{"class":114}," __getitem__",[90,893,894],{"class":104},"(self, i):       ",[90,896,675],{"class":96},[90,898,546],{"class":114},[90,900,901],{"class":104},"._cards[i]\n",[90,903,904],{"class":92,"line":157},[90,905,190],{"emptyLinePlaceholder":189},[90,907,908,911,913,916,919,921,924,926,929],{"class":92,"line":186},[90,909,910],{"class":104},"deck ",[90,912,136],{"class":96},[90,914,915],{"class":104}," Deck([",[90,917,918],{"class":330},"\"A\"",[90,920,22],{"class":104},[90,922,923],{"class":330},"\"K\"",[90,925,22],{"class":104},[90,927,928],{"class":330},"\"Q\"",[90,930,931],{"class":104},"])\n",[90,933,934,937,940],{"class":92,"line":193},[90,935,936],{"class":114},"len",[90,938,939],{"class":104},"(deck)        ",[90,941,942],{"class":153},"# 3\n",[90,944,945,948,951,954],{"class":92,"line":396},[90,946,947],{"class":104},"deck[",[90,949,950],{"class":114},"0",[90,952,953],{"class":104},"]          ",[90,955,956],{"class":153},"# 'A'\n",[90,958,959,962,965,967,970],{"class":92,"line":401},[90,960,961],{"class":96},"for",[90,963,964],{"class":104}," c ",[90,966,829],{"class":96},[90,968,969],{"class":104}," deck:   ",[90,971,972],{"class":153},"# iterable for free\n",[90,974,975,978],{"class":92,"line":421},[90,976,977],{"class":114},"    print",[90,979,980],{"class":104},"(c)\n",[15,982,983,984,79],{},"This is duck typing at its best: implement the protocol and the object is a sequence,\nwithout subclassing ",[19,985,986],{},"list",[10,988,990,993],{"id":989},"call-make-instances-callable",[38,991,992],{},"call",": make instances callable",[15,995,996,997,1000],{},"Defining ",[19,998,999],{},"__call__"," lets you call an instance like a function — useful for stateful\n\"function objects,\" decorators, and configurable callbacks.",[81,1002,1004],{"className":83,"code":1003,"language":85,"meta":86,"style":86},"class Multiplier:\n    def __init__(self, factor): self.factor = factor\n    def __call__(self, x):      return x * self.factor\n\ndouble = Multiplier(2)\ndouble(10)        # 20  — the instance behaves like a function\n",[19,1005,1006,1015,1034,1056,1060,1074],{"__ignoreMap":86},[90,1007,1008,1010,1013],{"class":92,"line":93},[90,1009,97],{"class":96},[90,1011,1012],{"class":100}," Multiplier",[90,1014,105],{"class":104},[90,1016,1017,1019,1021,1024,1026,1029,1031],{"class":92,"line":108},[90,1018,111],{"class":96},[90,1020,115],{"class":114},[90,1022,1023],{"class":104},"(self, factor): ",[90,1025,130],{"class":114},[90,1027,1028],{"class":104},".factor ",[90,1030,136],{"class":96},[90,1032,1033],{"class":104}," factor\n",[90,1035,1036,1038,1041,1044,1046,1049,1051,1053],{"class":92,"line":121},[90,1037,111],{"class":96},[90,1039,1040],{"class":114}," __call__",[90,1042,1043],{"class":104},"(self, x):      ",[90,1045,675],{"class":96},[90,1047,1048],{"class":104}," x ",[90,1050,622],{"class":96},[90,1052,546],{"class":114},[90,1054,1055],{"class":104},".factor\n",[90,1057,1058],{"class":92,"line":142},[90,1059,190],{"emptyLinePlaceholder":189},[90,1061,1062,1065,1067,1070,1072],{"class":92,"line":157},[90,1063,1064],{"class":104},"double ",[90,1066,136],{"class":96},[90,1068,1069],{"class":104}," Multiplier(",[90,1071,204],{"class":114},[90,1073,418],{"class":104},[90,1075,1076,1079,1082,1085],{"class":92,"line":186},[90,1077,1078],{"class":104},"double(",[90,1080,1081],{"class":114},"10",[90,1083,1084],{"class":104},")        ",[90,1086,1087],{"class":153},"# 20  — the instance behaves like a function\n",[10,1089,1091],{"id":1090},"context-manager-dunders","Context-manager dunders",[15,1093,1094,458,1097,1100,1101,1104,1105,1108],{},[19,1095,1096],{},"__enter__",[19,1098,1099],{},"__exit__"," make an object usable in a ",[19,1102,1103],{},"with"," block, guaranteeing cleanup.\nThat's the same protocol that powers ",[19,1106,1107],{},"open()"," — covered in depth in the context-managers\nguide.",[81,1110,1112],{"className":83,"code":1111,"language":85,"meta":86,"style":86},"class Timer:\n    def __enter__(self): import time; self.t = time.perf_counter(); return self\n    def __exit__(self, *exc): print(f\"{time.perf_counter() - self.t:.4f}s\")\n",[19,1113,1114,1123,1149],{"__ignoreMap":86},[90,1115,1116,1118,1121],{"class":92,"line":93},[90,1117,97],{"class":96},[90,1119,1120],{"class":100}," Timer",[90,1122,105],{"class":104},[90,1124,1125,1127,1130,1133,1136,1139,1141,1144,1146],{"class":92,"line":108},[90,1126,111],{"class":96},[90,1128,1129],{"class":114}," __enter__",[90,1131,1132],{"class":104},"(self): ",[90,1134,1135],{"class":96},"import",[90,1137,1138],{"class":104}," time; ",[90,1140,130],{"class":114},[90,1142,1143],{"class":104},".t = time.perf_counter(); ",[90,1145,675],{"class":96},[90,1147,1148],{"class":114}," self\n",[90,1150,1151,1153,1156,1159,1161,1164,1166,1168,1171,1174,1177,1180,1183,1185,1188,1191,1193,1196],{"class":92,"line":121},[90,1152,111],{"class":96},[90,1154,1155],{"class":114}," __exit__",[90,1157,1158],{"class":104},"(self, ",[90,1160,622],{"class":96},[90,1162,1163],{"class":104},"exc): ",[90,1165,267],{"class":114},[90,1167,572],{"class":104},[90,1169,1170],{"class":96},"f",[90,1172,1173],{"class":330},"\"",[90,1175,1176],{"class":114},"{",[90,1178,1179],{"class":104},"time.perf_counter() ",[90,1181,1182],{"class":96},"-",[90,1184,546],{"class":114},[90,1186,1187],{"class":104},".t",[90,1189,1190],{"class":96},":.4f",[90,1192,340],{"class":114},[90,1194,1195],{"class":330},"s\"",[90,1197,418],{"class":104},[10,1199,1201],{"id":1200},"recap","Recap",[15,1203,1204,1205,1208,1209,1211,1212,32,1214,1216,1217,1219,1220,1222,1223,1225,1226,264,1228,1230,1231,171,1233,1235,1236,1238,1239,1241],{},"Dunder methods are the ",[38,1206,1207],{},"hooks"," Python calls for built-in syntax — they're how user\nclasses plug into ",[19,1210,814],{},", operators, indexing, iteration, ",[19,1213,267],{},[19,1215,1103],{},". Always\ngive a class a ",[19,1218,248],{},"; pair ",[19,1221,467],{}," with a consistent ",[19,1224,475],{}," (and hash only on\nimmutable fields); overload operators with ",[19,1227,619],{},[19,1229,632],{},"\u002Fetc.; implement\n",[19,1232,48],{},[19,1234,810],{}," for sequence behaviour; and use ",[19,1237,999],{}," for callable instances.\nThink ",[38,1240,231],{}," — implement the right dunders and your objects feel\nnative.",[1243,1244,1245],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":86,"searchDepth":108,"depth":108,"links":1247},[1248,1249,1250,1252,1254,1255,1256,1258,1259],{"id":12,"depth":108,"text":13},{"id":52,"depth":108,"text":53},{"id":235,"depth":108,"text":1251},"repr vs str",{"id":454,"depth":108,"text":1253},"eq and hash go together",{"id":609,"depth":108,"text":610},{"id":801,"depth":108,"text":802},{"id":989,"depth":108,"text":1257},"call: make instances callable",{"id":1090,"depth":108,"text":1091},{"id":1200,"depth":108,"text":1201},"How Python's dunder (magic) methods work — __repr__ vs __str__, __eq__ and __hash__, operator overloading, the sequence protocol, __call__, and how they hook into the data model.","hard","md","Python",{},"\u002Fblog\u002Fpython-dunder-magic-methods-explained","\u002Fpython\u002Foop\u002Fdunder-methods",{"title":5,"description":1260},"blog\u002Fpython-dunder-magic-methods-explained","Dunder \u002F Magic Methods","Object-Oriented Programming","oop","2026-06-19","wL6aBGwKWsNyOtju7Nuyh_iovmoY3T1WKLsDrHSmtEY",1781808673081]