[{"data":1,"prerenderedAt":2557},["ShallowReactive",2],{"page-\u002Fpython\u002F13-inheritance-and-polymorphism":3},{"id":4,"title":5,"body":6,"description":27,"extension":2551,"meta":2552,"navigation":81,"path":2553,"seo":2554,"stem":2555,"__hash__":2556},"content\u002Fpython\u002F13-inheritance-and-polymorphism.md","13 — Inheritance & Polymorphism",{"type":7,"value":8,"toc":2528},"minimark",[9,13,18,290,318,325,544,557,561,677,684,688,695,850,874,879,935,957,963,968,1120,1146,1150,1164,1454,1471,1480,1570,1584,1591,1723,1741,1751,1918,1922,2002,2006,2083,2087,2090,2306,2479,2483,2524],[10,11,5],"h1",{"id":12},"_13-inheritance-polymorphism",[14,15,17],"h2",{"id":16},"single-inheritance-basics","Single Inheritance Basics",[19,20,22],"code-wrapper",{"language":21},"python",[23,24,28],"pre",{"className":25,"code":26,"language":21,"meta":27,"style":27},"language-python shiki shiki-themes github-light github-dark","class Animal:\n    def __init__(self, name):\n        self.name = name\n\n    def speak(self):\n        raise NotImplementedError(\"Subclasses must implement speak()\")\n\n    def describe(self):\n        return f\"{self.name} says {self.speak()}\"\n\nclass Dog(Animal):\n    def speak(self):\n        return \"Woof!\"\n\nclass Cat(Animal):\n    def speak(self):\n        return \"Meow!\"\n\nfor animal in [Dog(\"Rex\"), Cat(\"Whiskers\")]:\n    print(animal.describe())\n# Rex says Woof!\n# Whiskers says Meow!\n","",[29,30,31,48,61,76,83,94,113,118,128,162,167,183,192,200,205,219,228,236,241,268,277,284],"code",{"__ignoreMap":27},[32,33,36,40,44],"span",{"class":34,"line":35},"line",1,[32,37,39],{"class":38},"svdQ7","class",[32,41,43],{"class":42},"sIsaT"," Animal",[32,45,47],{"class":46},"ssxIu",":\n",[32,49,51,54,58],{"class":34,"line":50},2,[32,52,53],{"class":38},"    def",[32,55,57],{"class":56},"snvgF"," __init__",[32,59,60],{"class":46},"(self, name):\n",[32,62,64,67,70,73],{"class":34,"line":63},3,[32,65,66],{"class":56},"        self",[32,68,69],{"class":46},".name ",[32,71,72],{"class":38},"=",[32,74,75],{"class":46}," name\n",[32,77,79],{"class":34,"line":78},4,[32,80,82],{"emptyLinePlaceholder":81},true,"\n",[32,84,86,88,91],{"class":34,"line":85},5,[32,87,53],{"class":38},[32,89,90],{"class":42}," speak",[32,92,93],{"class":46},"(self):\n",[32,95,97,100,103,106,110],{"class":34,"line":96},6,[32,98,99],{"class":38},"        raise",[32,101,102],{"class":56}," NotImplementedError",[32,104,105],{"class":46},"(",[32,107,109],{"class":108},"sJ6F3","\"Subclasses must implement speak()\"",[32,111,112],{"class":46},")\n",[32,114,116],{"class":34,"line":115},7,[32,117,82],{"emptyLinePlaceholder":81},[32,119,121,123,126],{"class":34,"line":120},8,[32,122,53],{"class":38},[32,124,125],{"class":42}," describe",[32,127,93],{"class":46},[32,129,131,134,137,140,143,146,149,152,154,157,159],{"class":34,"line":130},9,[32,132,133],{"class":38},"        return",[32,135,136],{"class":38}," f",[32,138,139],{"class":108},"\"",[32,141,142],{"class":56},"{self",[32,144,145],{"class":46},".name",[32,147,148],{"class":56},"}",[32,150,151],{"class":108}," says ",[32,153,142],{"class":56},[32,155,156],{"class":46},".speak()",[32,158,148],{"class":56},[32,160,161],{"class":108},"\"\n",[32,163,165],{"class":34,"line":164},10,[32,166,82],{"emptyLinePlaceholder":81},[32,168,170,172,175,177,180],{"class":34,"line":169},11,[32,171,39],{"class":38},[32,173,174],{"class":42}," Dog",[32,176,105],{"class":46},[32,178,179],{"class":42},"Animal",[32,181,182],{"class":46},"):\n",[32,184,186,188,190],{"class":34,"line":185},12,[32,187,53],{"class":38},[32,189,90],{"class":42},[32,191,93],{"class":46},[32,193,195,197],{"class":34,"line":194},13,[32,196,133],{"class":38},[32,198,199],{"class":108}," \"Woof!\"\n",[32,201,203],{"class":34,"line":202},14,[32,204,82],{"emptyLinePlaceholder":81},[32,206,208,210,213,215,217],{"class":34,"line":207},15,[32,209,39],{"class":38},[32,211,212],{"class":42}," Cat",[32,214,105],{"class":46},[32,216,179],{"class":42},[32,218,182],{"class":46},[32,220,222,224,226],{"class":34,"line":221},16,[32,223,53],{"class":38},[32,225,90],{"class":42},[32,227,93],{"class":46},[32,229,231,233],{"class":34,"line":230},17,[32,232,133],{"class":38},[32,234,235],{"class":108}," \"Meow!\"\n",[32,237,239],{"class":34,"line":238},18,[32,240,82],{"emptyLinePlaceholder":81},[32,242,244,247,250,253,256,259,262,265],{"class":34,"line":243},19,[32,245,246],{"class":38},"for",[32,248,249],{"class":46}," animal ",[32,251,252],{"class":38},"in",[32,254,255],{"class":46}," [Dog(",[32,257,258],{"class":108},"\"Rex\"",[32,260,261],{"class":46},"), Cat(",[32,263,264],{"class":108},"\"Whiskers\"",[32,266,267],{"class":46},")]:\n",[32,269,271,274],{"class":34,"line":270},20,[32,272,273],{"class":56},"    print",[32,275,276],{"class":46},"(animal.describe())\n",[32,278,280],{"class":34,"line":279},21,[32,281,283],{"class":282},"sdCPZ","# Rex says Woof!\n",[32,285,287],{"class":34,"line":286},22,[32,288,289],{"class":282},"# Whiskers says Meow!\n",[291,292,293,294,298,299,302,303,305,306,309,310,313,314,317],"p",{},"This is ",[295,296,297],"strong",{},"polymorphism",": ",[29,300,301],{},"describe()"," is defined once, on ",[29,304,179],{},", but calls ",[29,307,308],{},"self.speak()"," — which resolves to whichever subclass's ",[29,311,312],{},"speak"," the actual instance has, at call time. Python doesn't need an ",[29,315,316],{},"interface"," keyword or explicit virtual dispatch declarations — every method call is dynamically dispatched based on the object's actual type.",[14,319,321,324],{"id":320},"super-calling-the-parents-implementation",[29,322,323],{},"super()"," — Calling the Parent's Implementation",[19,326,327],{"language":21},[23,328,330],{"className":25,"code":329,"language":21,"meta":27,"style":27},"class Employee:\n    def __init__(self, name, salary):\n        self.name = name\n        self.salary = salary\n\n    def annual_bonus(self):\n        return self.salary * 0.05\n\nclass Manager(Employee):\n    def __init__(self, name, salary, team_size):\n        super().__init__(name, salary)     # delegates to Employee.__init__\n        self.team_size = team_size\n\n    def annual_bonus(self):\n        base_bonus = super().annual_bonus()   # reuses parent logic, then extends it\n        return base_bonus + self.team_size * 100\n\nm = Manager(\"Priya\", 90_000, 5)\nprint(m.annual_bonus())   # 90000*0.05 + 5*100 = 4500 + 500 = 5000.0\n",[29,331,332,341,350,360,372,376,385,400,404,418,427,444,456,460,468,484,503,507,533],{"__ignoreMap":27},[32,333,334,336,339],{"class":34,"line":35},[32,335,39],{"class":38},[32,337,338],{"class":42}," Employee",[32,340,47],{"class":46},[32,342,343,345,347],{"class":34,"line":50},[32,344,53],{"class":38},[32,346,57],{"class":56},[32,348,349],{"class":46},"(self, name, salary):\n",[32,351,352,354,356,358],{"class":34,"line":63},[32,353,66],{"class":56},[32,355,69],{"class":46},[32,357,72],{"class":38},[32,359,75],{"class":46},[32,361,362,364,367,369],{"class":34,"line":78},[32,363,66],{"class":56},[32,365,366],{"class":46},".salary ",[32,368,72],{"class":38},[32,370,371],{"class":46}," salary\n",[32,373,374],{"class":34,"line":85},[32,375,82],{"emptyLinePlaceholder":81},[32,377,378,380,383],{"class":34,"line":96},[32,379,53],{"class":38},[32,381,382],{"class":42}," annual_bonus",[32,384,93],{"class":46},[32,386,387,389,392,394,397],{"class":34,"line":115},[32,388,133],{"class":38},[32,390,391],{"class":56}," self",[32,393,366],{"class":46},[32,395,396],{"class":38},"*",[32,398,399],{"class":56}," 0.05\n",[32,401,402],{"class":34,"line":120},[32,403,82],{"emptyLinePlaceholder":81},[32,405,406,408,411,413,416],{"class":34,"line":130},[32,407,39],{"class":38},[32,409,410],{"class":42}," Manager",[32,412,105],{"class":46},[32,414,415],{"class":42},"Employee",[32,417,182],{"class":46},[32,419,420,422,424],{"class":34,"line":164},[32,421,53],{"class":38},[32,423,57],{"class":56},[32,425,426],{"class":46},"(self, name, salary, team_size):\n",[32,428,429,432,435,438,441],{"class":34,"line":169},[32,430,431],{"class":56},"        super",[32,433,434],{"class":46},"().",[32,436,437],{"class":56},"__init__",[32,439,440],{"class":46},"(name, salary)     ",[32,442,443],{"class":282},"# delegates to Employee.__init__\n",[32,445,446,448,451,453],{"class":34,"line":185},[32,447,66],{"class":56},[32,449,450],{"class":46},".team_size ",[32,452,72],{"class":38},[32,454,455],{"class":46}," team_size\n",[32,457,458],{"class":34,"line":194},[32,459,82],{"emptyLinePlaceholder":81},[32,461,462,464,466],{"class":34,"line":202},[32,463,53],{"class":38},[32,465,382],{"class":42},[32,467,93],{"class":46},[32,469,470,473,475,478,481],{"class":34,"line":207},[32,471,472],{"class":46},"        base_bonus ",[32,474,72],{"class":38},[32,476,477],{"class":56}," super",[32,479,480],{"class":46},"().annual_bonus()   ",[32,482,483],{"class":282},"# reuses parent logic, then extends it\n",[32,485,486,488,491,494,496,498,500],{"class":34,"line":221},[32,487,133],{"class":38},[32,489,490],{"class":46}," base_bonus ",[32,492,493],{"class":38},"+",[32,495,391],{"class":56},[32,497,450],{"class":46},[32,499,396],{"class":38},[32,501,502],{"class":56}," 100\n",[32,504,505],{"class":34,"line":230},[32,506,82],{"emptyLinePlaceholder":81},[32,508,509,512,514,517,520,523,526,528,531],{"class":34,"line":238},[32,510,511],{"class":46},"m ",[32,513,72],{"class":38},[32,515,516],{"class":46}," Manager(",[32,518,519],{"class":108},"\"Priya\"",[32,521,522],{"class":46},", ",[32,524,525],{"class":56},"90_000",[32,527,522],{"class":46},[32,529,530],{"class":56},"5",[32,532,112],{"class":46},[32,534,535,538,541],{"class":34,"line":243},[32,536,537],{"class":56},"print",[32,539,540],{"class":46},"(m.annual_bonus())   ",[32,542,543],{"class":282},"# 90000*0.05 + 5*100 = 4500 + 500 = 5000.0\n",[291,545,546,549,550,553,554,556],{},[295,547,548],{},"Best practice",": always call ",[29,551,552],{},"super().__init__(...)"," in a subclass's ",[29,555,437],{}," unless you have a specific, deliberate reason not to (a common pattern in mixins) — skipping it means the parent's setup logic silently never runs, leaving the instance in a partially-initialized state that only fails later, far from the actual bug.",[14,558,560],{"id":559},"multiple-inheritance-and-the-diamond-problem","Multiple Inheritance and the Diamond Problem",[19,562,563],{"language":21},[23,564,566],{"className":25,"code":565,"language":21,"meta":27,"style":27},"class Flyer:\n    def move(self):\n        return \"flies\"\n\nclass Swimmer:\n    def move(self):\n        return \"swims\"\n\nclass Duck(Flyer, Swimmer):    # inherits from BOTH\n    pass\n\nd = Duck()\nprint(d.move())   # \"flies\" — Flyer comes first in the class definition\n",[29,567,568,577,586,593,597,606,614,621,625,648,653,657,667],{"__ignoreMap":27},[32,569,570,572,575],{"class":34,"line":35},[32,571,39],{"class":38},[32,573,574],{"class":42}," Flyer",[32,576,47],{"class":46},[32,578,579,581,584],{"class":34,"line":50},[32,580,53],{"class":38},[32,582,583],{"class":42}," move",[32,585,93],{"class":46},[32,587,588,590],{"class":34,"line":63},[32,589,133],{"class":38},[32,591,592],{"class":108}," \"flies\"\n",[32,594,595],{"class":34,"line":78},[32,596,82],{"emptyLinePlaceholder":81},[32,598,599,601,604],{"class":34,"line":85},[32,600,39],{"class":38},[32,602,603],{"class":42}," Swimmer",[32,605,47],{"class":46},[32,607,608,610,612],{"class":34,"line":96},[32,609,53],{"class":38},[32,611,583],{"class":42},[32,613,93],{"class":46},[32,615,616,618],{"class":34,"line":115},[32,617,133],{"class":38},[32,619,620],{"class":108}," \"swims\"\n",[32,622,623],{"class":34,"line":120},[32,624,82],{"emptyLinePlaceholder":81},[32,626,627,629,632,634,637,639,642,645],{"class":34,"line":130},[32,628,39],{"class":38},[32,630,631],{"class":42}," Duck",[32,633,105],{"class":46},[32,635,636],{"class":42},"Flyer",[32,638,522],{"class":46},[32,640,641],{"class":42},"Swimmer",[32,643,644],{"class":46},"):    ",[32,646,647],{"class":282},"# inherits from BOTH\n",[32,649,650],{"class":34,"line":164},[32,651,652],{"class":38},"    pass\n",[32,654,655],{"class":34,"line":169},[32,656,82],{"emptyLinePlaceholder":81},[32,658,659,662,664],{"class":34,"line":185},[32,660,661],{"class":46},"d ",[32,663,72],{"class":38},[32,665,666],{"class":46}," Duck()\n",[32,668,669,671,674],{"class":34,"line":194},[32,670,537],{"class":56},[32,672,673],{"class":46},"(d.move())   ",[32,675,676],{"class":282},"# \"flies\" — Flyer comes first in the class definition\n",[291,678,679,680,683],{},"When two parent classes define the same method, which one wins is determined by the ",[295,681,682],{},"Method Resolution Order (MRO)"," — not simply \"leftmost parent\" by coincidence, but a well-defined algorithm.",[14,685,687],{"id":686},"mro-and-c3-linearization","MRO and C3 Linearization",[291,689,690,691,694],{},"Python uses the ",[295,692,693],{},"C3 linearization"," algorithm to compute a single, consistent method lookup order across arbitrarily complex inheritance graphs, avoiding the ambiguities that plague simpler \"depth-first\" schemes.",[19,696,697],{"language":21},[23,698,700],{"className":25,"code":699,"language":21,"meta":27,"style":27},"class A:\n    def greet(self):\n        return \"A\"\n\nclass B(A):\n    def greet(self):\n        return \"B\"\n\nclass C(A):\n    def greet(self):\n        return \"C\"\n\nclass D(B, C):\n    pass\n\nprint(D.__mro__)\n# (\u003Cclass 'D'>, \u003Cclass 'B'>, \u003Cclass 'C'>, \u003Cclass 'A'>, \u003Cclass 'object'>)\nprint(D().greet())   # \"B\" — first class in the MRO that defines greet()\n",[29,701,702,711,720,727,731,745,753,760,764,777,785,792,796,815,819,823,835,840],{"__ignoreMap":27},[32,703,704,706,709],{"class":34,"line":35},[32,705,39],{"class":38},[32,707,708],{"class":42}," A",[32,710,47],{"class":46},[32,712,713,715,718],{"class":34,"line":50},[32,714,53],{"class":38},[32,716,717],{"class":42}," greet",[32,719,93],{"class":46},[32,721,722,724],{"class":34,"line":63},[32,723,133],{"class":38},[32,725,726],{"class":108}," \"A\"\n",[32,728,729],{"class":34,"line":78},[32,730,82],{"emptyLinePlaceholder":81},[32,732,733,735,738,740,743],{"class":34,"line":85},[32,734,39],{"class":38},[32,736,737],{"class":42}," B",[32,739,105],{"class":46},[32,741,742],{"class":42},"A",[32,744,182],{"class":46},[32,746,747,749,751],{"class":34,"line":96},[32,748,53],{"class":38},[32,750,717],{"class":42},[32,752,93],{"class":46},[32,754,755,757],{"class":34,"line":115},[32,756,133],{"class":38},[32,758,759],{"class":108}," \"B\"\n",[32,761,762],{"class":34,"line":120},[32,763,82],{"emptyLinePlaceholder":81},[32,765,766,768,771,773,775],{"class":34,"line":130},[32,767,39],{"class":38},[32,769,770],{"class":42}," C",[32,772,105],{"class":46},[32,774,742],{"class":42},[32,776,182],{"class":46},[32,778,779,781,783],{"class":34,"line":164},[32,780,53],{"class":38},[32,782,717],{"class":42},[32,784,93],{"class":46},[32,786,787,789],{"class":34,"line":169},[32,788,133],{"class":38},[32,790,791],{"class":108}," \"C\"\n",[32,793,794],{"class":34,"line":185},[32,795,82],{"emptyLinePlaceholder":81},[32,797,798,800,803,805,808,810,813],{"class":34,"line":194},[32,799,39],{"class":38},[32,801,802],{"class":42}," D",[32,804,105],{"class":46},[32,806,807],{"class":42},"B",[32,809,522],{"class":46},[32,811,812],{"class":42},"C",[32,814,182],{"class":46},[32,816,817],{"class":34,"line":202},[32,818,652],{"class":38},[32,820,821],{"class":34,"line":207},[32,822,82],{"emptyLinePlaceholder":81},[32,824,825,827,830,833],{"class":34,"line":221},[32,826,537],{"class":56},[32,828,829],{"class":46},"(D.",[32,831,832],{"class":56},"__mro__",[32,834,112],{"class":46},[32,836,837],{"class":34,"line":230},[32,838,839],{"class":282},"# (\u003Cclass 'D'>, \u003Cclass 'B'>, \u003Cclass 'C'>, \u003Cclass 'A'>, \u003Cclass 'object'>)\n",[32,841,842,844,847],{"class":34,"line":238},[32,843,537],{"class":56},[32,845,846],{"class":46},"(D().greet())   ",[32,848,849],{"class":282},"# \"B\" — first class in the MRO that defines greet()\n",[291,851,852,853,856,857,860,861,864,865,867,868,870,871,873],{},"The rule C3 guarantees: a class always appears before its parents, and the order of parents as listed in the class definition is preserved. This is why ",[29,854,855],{},"D(B, C)"," produces ",[29,858,859],{},"[D, B, C, A, object]"," rather than the naive depth-first ",[29,862,863],{},"[D, B, A, C, A, object]"," (which visits ",[29,866,742],{}," twice, and doesn't respect the constraint that ",[29,869,812],{}," must come before ",[29,872,742],{},").",[875,876,878],"h3",{"id":877},"when-c3-linearization-is-impossible","When C3 linearization is impossible",[19,880,881],{"language":21},[23,882,884],{"className":25,"code":883,"language":21,"meta":27,"style":27},"class X:\n    pass\n\nclass Y(X):\n    pass\n\n# class Z(X, Y):   # TypeError: Cannot create a consistent method resolution order (MRO)\n#     pass\n",[29,885,886,895,899,903,917,921,925,930],{"__ignoreMap":27},[32,887,888,890,893],{"class":34,"line":35},[32,889,39],{"class":38},[32,891,892],{"class":42}," X",[32,894,47],{"class":46},[32,896,897],{"class":34,"line":50},[32,898,652],{"class":38},[32,900,901],{"class":34,"line":63},[32,902,82],{"emptyLinePlaceholder":81},[32,904,905,907,910,912,915],{"class":34,"line":78},[32,906,39],{"class":38},[32,908,909],{"class":42}," Y",[32,911,105],{"class":46},[32,913,914],{"class":42},"X",[32,916,182],{"class":46},[32,918,919],{"class":34,"line":85},[32,920,652],{"class":38},[32,922,923],{"class":34,"line":96},[32,924,82],{"emptyLinePlaceholder":81},[32,926,927],{"class":34,"line":115},[32,928,929],{"class":282},"# class Z(X, Y):   # TypeError: Cannot create a consistent method resolution order (MRO)\n",[32,931,932],{"class":34,"line":120},[32,933,934],{"class":282},"#     pass\n",[291,936,937,940,941,943,944,947,948,950,951,953,954,956],{},[29,938,939],{},"class Z(X, Y)"," demands that ",[29,942,914],{}," come before ",[29,945,946],{},"Y"," (as listed) while also requiring ",[29,949,946],{}," (a subclass of ",[29,952,914],{},") to come before its own parent ",[29,955,914],{}," — a genuine contradiction. Python detects this at class-creation time and refuses to create the class, rather than silently picking an arbitrary, possibly-surprising order.",[14,958,960,961],{"id":959},"cooperative-multiple-inheritance-with-super","Cooperative Multiple Inheritance with ",[29,962,323],{},[291,964,965,967],{},[29,966,323],{}," in a multi-inheritance hierarchy doesn't mean \"my direct parent\" — it means \"the next class in the MRO,\" which is essential to understand for mixins that all need to run.",[19,969,970],{"language":21},[23,971,973],{"className":25,"code":972,"language":21,"meta":27,"style":27},"class LoggingMixin:\n    def save(self):\n        print(\"Logging save operation\")\n        super().save()          # cooperatively passes control along the MRO\n\nclass ValidationMixin:\n    def save(self):\n        print(\"Validating before save\")\n        super().save()\n\nclass Document(LoggingMixin, ValidationMixin):\n    def save(self):\n        print(\"Saving document\")\n\nDocument().save()\n# Logging save operation\n# Validating before save\n# Saving document\n",[29,974,975,984,993,1005,1015,1019,1028,1036,1047,1054,1058,1077,1085,1096,1100,1105,1110,1115],{"__ignoreMap":27},[32,976,977,979,982],{"class":34,"line":35},[32,978,39],{"class":38},[32,980,981],{"class":42}," LoggingMixin",[32,983,47],{"class":46},[32,985,986,988,991],{"class":34,"line":50},[32,987,53],{"class":38},[32,989,990],{"class":42}," save",[32,992,93],{"class":46},[32,994,995,998,1000,1003],{"class":34,"line":63},[32,996,997],{"class":56},"        print",[32,999,105],{"class":46},[32,1001,1002],{"class":108},"\"Logging save operation\"",[32,1004,112],{"class":46},[32,1006,1007,1009,1012],{"class":34,"line":78},[32,1008,431],{"class":56},[32,1010,1011],{"class":46},"().save()          ",[32,1013,1014],{"class":282},"# cooperatively passes control along the MRO\n",[32,1016,1017],{"class":34,"line":85},[32,1018,82],{"emptyLinePlaceholder":81},[32,1020,1021,1023,1026],{"class":34,"line":96},[32,1022,39],{"class":38},[32,1024,1025],{"class":42}," ValidationMixin",[32,1027,47],{"class":46},[32,1029,1030,1032,1034],{"class":34,"line":115},[32,1031,53],{"class":38},[32,1033,990],{"class":42},[32,1035,93],{"class":46},[32,1037,1038,1040,1042,1045],{"class":34,"line":120},[32,1039,997],{"class":56},[32,1041,105],{"class":46},[32,1043,1044],{"class":108},"\"Validating before save\"",[32,1046,112],{"class":46},[32,1048,1049,1051],{"class":34,"line":130},[32,1050,431],{"class":56},[32,1052,1053],{"class":46},"().save()\n",[32,1055,1056],{"class":34,"line":164},[32,1057,82],{"emptyLinePlaceholder":81},[32,1059,1060,1062,1065,1067,1070,1072,1075],{"class":34,"line":169},[32,1061,39],{"class":38},[32,1063,1064],{"class":42}," Document",[32,1066,105],{"class":46},[32,1068,1069],{"class":42},"LoggingMixin",[32,1071,522],{"class":46},[32,1073,1074],{"class":42},"ValidationMixin",[32,1076,182],{"class":46},[32,1078,1079,1081,1083],{"class":34,"line":185},[32,1080,53],{"class":38},[32,1082,990],{"class":42},[32,1084,93],{"class":46},[32,1086,1087,1089,1091,1094],{"class":34,"line":194},[32,1088,997],{"class":56},[32,1090,105],{"class":46},[32,1092,1093],{"class":108},"\"Saving document\"",[32,1095,112],{"class":46},[32,1097,1098],{"class":34,"line":202},[32,1099,82],{"emptyLinePlaceholder":81},[32,1101,1102],{"class":34,"line":207},[32,1103,1104],{"class":46},"Document().save()\n",[32,1106,1107],{"class":34,"line":221},[32,1108,1109],{"class":282},"# Logging save operation\n",[32,1111,1112],{"class":34,"line":230},[32,1113,1114],{"class":282},"# Validating before save\n",[32,1116,1117],{"class":34,"line":238},[32,1118,1119],{"class":282},"# Saving document\n",[291,1121,1122,1125,1126,1129,1130,1133,1134,1138,1139,1142,1143,1145],{},[29,1123,1124],{},"Document.__mro__"," is ",[29,1127,1128],{},"[Document, LoggingMixin, ValidationMixin, object]",". Each ",[29,1131,1132],{},"super().save()"," call advances to the ",[1135,1136,1137],"em",{},"next"," class in that MRO — not necessarily each class's own direct parent — which is why all three ",[29,1140,1141],{},"save"," methods run in sequence even though none of these mixins directly inherits from another. This pattern requires every cooperating class to call ",[29,1144,323],{}," — a mixin that forgets to call it breaks the chain for everyone after it.",[14,1147,1149],{"id":1148},"abstract-base-classes-abc","Abstract Base Classes (ABC)",[291,1151,1152,1155,1156,1159,1160,1163],{},[29,1153,1154],{},"abc.ABC"," and ",[29,1157,1158],{},"@abstractmethod"," let you declare an interface that ",[295,1161,1162],{},"cannot be instantiated"," until all abstract methods are implemented, catching incomplete implementations at instantiation time rather than at first use.",[19,1165,1166],{"language":21},[23,1167,1169],{"className":25,"code":1168,"language":21,"meta":27,"style":27},"from abc import ABC, abstractmethod\n\nclass Shape(ABC):\n    @abstractmethod\n    def area(self):\n        ...\n\n    @abstractmethod\n    def perimeter(self):\n        ...\n\n    def describe(self):                 # concrete methods are allowed alongside abstract ones\n        return f\"Area: {self.area()}, Perimeter: {self.perimeter()}\"\n\n# shape = Shape()   # TypeError: Can't instantiate abstract class Shape\n#                     with abstract methods area, perimeter\n\nclass Rectangle(Shape):\n    def __init__(self, w, h):\n        self.w, self.h = w, h\n\n    def area(self):\n        return self.w * self.h\n\n    def perimeter(self):\n        return 2 * (self.w + self.h)\n\nr = Rectangle(3, 4)\nprint(r.describe())   # Area: 12, Perimeter: 14\n",[29,1170,1171,1188,1192,1206,1211,1220,1225,1229,1233,1242,1246,1250,1262,1290,1294,1299,1304,1308,1322,1331,1349,1353,1361,1378,1383,1392,1417,1422,1443],{"__ignoreMap":27},[32,1172,1173,1176,1179,1182,1185],{"class":34,"line":35},[32,1174,1175],{"class":38},"from",[32,1177,1178],{"class":46}," abc ",[32,1180,1181],{"class":38},"import",[32,1183,1184],{"class":56}," ABC",[32,1186,1187],{"class":46},", abstractmethod\n",[32,1189,1190],{"class":34,"line":50},[32,1191,82],{"emptyLinePlaceholder":81},[32,1193,1194,1196,1199,1201,1204],{"class":34,"line":63},[32,1195,39],{"class":38},[32,1197,1198],{"class":42}," Shape",[32,1200,105],{"class":46},[32,1202,1203],{"class":56},"ABC",[32,1205,182],{"class":46},[32,1207,1208],{"class":34,"line":78},[32,1209,1210],{"class":42},"    @abstractmethod\n",[32,1212,1213,1215,1218],{"class":34,"line":85},[32,1214,53],{"class":38},[32,1216,1217],{"class":42}," area",[32,1219,93],{"class":46},[32,1221,1222],{"class":34,"line":96},[32,1223,1224],{"class":56},"        ...\n",[32,1226,1227],{"class":34,"line":115},[32,1228,82],{"emptyLinePlaceholder":81},[32,1230,1231],{"class":34,"line":120},[32,1232,1210],{"class":42},[32,1234,1235,1237,1240],{"class":34,"line":130},[32,1236,53],{"class":38},[32,1238,1239],{"class":42}," perimeter",[32,1241,93],{"class":46},[32,1243,1244],{"class":34,"line":164},[32,1245,1224],{"class":56},[32,1247,1248],{"class":34,"line":169},[32,1249,82],{"emptyLinePlaceholder":81},[32,1251,1252,1254,1256,1259],{"class":34,"line":185},[32,1253,53],{"class":38},[32,1255,125],{"class":42},[32,1257,1258],{"class":46},"(self):                 ",[32,1260,1261],{"class":282},"# concrete methods are allowed alongside abstract ones\n",[32,1263,1264,1266,1268,1271,1273,1276,1278,1281,1283,1286,1288],{"class":34,"line":194},[32,1265,133],{"class":38},[32,1267,136],{"class":38},[32,1269,1270],{"class":108},"\"Area: ",[32,1272,142],{"class":56},[32,1274,1275],{"class":46},".area()",[32,1277,148],{"class":56},[32,1279,1280],{"class":108},", Perimeter: ",[32,1282,142],{"class":56},[32,1284,1285],{"class":46},".perimeter()",[32,1287,148],{"class":56},[32,1289,161],{"class":108},[32,1291,1292],{"class":34,"line":202},[32,1293,82],{"emptyLinePlaceholder":81},[32,1295,1296],{"class":34,"line":207},[32,1297,1298],{"class":282},"# shape = Shape()   # TypeError: Can't instantiate abstract class Shape\n",[32,1300,1301],{"class":34,"line":221},[32,1302,1303],{"class":282},"#                     with abstract methods area, perimeter\n",[32,1305,1306],{"class":34,"line":230},[32,1307,82],{"emptyLinePlaceholder":81},[32,1309,1310,1312,1315,1317,1320],{"class":34,"line":238},[32,1311,39],{"class":38},[32,1313,1314],{"class":42}," Rectangle",[32,1316,105],{"class":46},[32,1318,1319],{"class":42},"Shape",[32,1321,182],{"class":46},[32,1323,1324,1326,1328],{"class":34,"line":243},[32,1325,53],{"class":38},[32,1327,57],{"class":56},[32,1329,1330],{"class":46},"(self, w, h):\n",[32,1332,1333,1335,1338,1341,1344,1346],{"class":34,"line":270},[32,1334,66],{"class":56},[32,1336,1337],{"class":46},".w, ",[32,1339,1340],{"class":56},"self",[32,1342,1343],{"class":46},".h ",[32,1345,72],{"class":38},[32,1347,1348],{"class":46}," w, h\n",[32,1350,1351],{"class":34,"line":279},[32,1352,82],{"emptyLinePlaceholder":81},[32,1354,1355,1357,1359],{"class":34,"line":286},[32,1356,53],{"class":38},[32,1358,1217],{"class":42},[32,1360,93],{"class":46},[32,1362,1364,1366,1368,1371,1373,1375],{"class":34,"line":1363},23,[32,1365,133],{"class":38},[32,1367,391],{"class":56},[32,1369,1370],{"class":46},".w ",[32,1372,396],{"class":38},[32,1374,391],{"class":56},[32,1376,1377],{"class":46},".h\n",[32,1379,1381],{"class":34,"line":1380},24,[32,1382,82],{"emptyLinePlaceholder":81},[32,1384,1386,1388,1390],{"class":34,"line":1385},25,[32,1387,53],{"class":38},[32,1389,1239],{"class":42},[32,1391,93],{"class":46},[32,1393,1395,1397,1400,1403,1406,1408,1410,1412,1414],{"class":34,"line":1394},26,[32,1396,133],{"class":38},[32,1398,1399],{"class":56}," 2",[32,1401,1402],{"class":38}," *",[32,1404,1405],{"class":46}," (",[32,1407,1340],{"class":56},[32,1409,1370],{"class":46},[32,1411,493],{"class":38},[32,1413,391],{"class":56},[32,1415,1416],{"class":46},".h)\n",[32,1418,1420],{"class":34,"line":1419},27,[32,1421,82],{"emptyLinePlaceholder":81},[32,1423,1425,1428,1430,1433,1436,1438,1441],{"class":34,"line":1424},28,[32,1426,1427],{"class":46},"r ",[32,1429,72],{"class":38},[32,1431,1432],{"class":46}," Rectangle(",[32,1434,1435],{"class":56},"3",[32,1437,522],{"class":46},[32,1439,1440],{"class":56},"4",[32,1442,112],{"class":46},[32,1444,1446,1448,1451],{"class":34,"line":1445},29,[32,1447,537],{"class":56},[32,1449,1450],{"class":46},"(r.describe())   ",[32,1452,1453],{"class":282},"# Area: 12, Perimeter: 14\n",[291,1455,1456,1457,1460,1461,1464,1465,1467,1468,1470],{},"Compare to the \"raise ",[29,1458,1459],{},"NotImplementedError","\" pattern from the first example in this chapter: that only fails when the missing method is actually ",[1135,1462,1463],{},"called",", potentially deep in production after the object was already constructed and passed around — ",[29,1466,1203],{}," fails immediately at ",[29,1469,437],{}," time, which is a strictly earlier and safer failure point.",[875,1472,1474,1476,1477],{"id":1473},"abc-vs-duck-typing-vs-protocol",[29,1475,1203],{}," vs duck typing vs ",[29,1478,1479],{},"Protocol",[19,1481,1482],{"language":21},[23,1483,1485],{"className":25,"code":1484,"language":21,"meta":27,"style":27},"# Duck typing — no formal contract, just \"if it has the method, it works\"\nclass DuckTypedShape:\n    def area(self):\n        return 42\n\ndef print_area(shape):\n    print(shape.area())   # works on ANY object with an area() method, no inheritance required\n\nprint_area(DuckTypedShape())   # 42\nprint_area(Rectangle(3, 4))      # 12\n",[29,1486,1487,1492,1501,1509,1516,1520,1531,1541,1545,1553],{"__ignoreMap":27},[32,1488,1489],{"class":34,"line":35},[32,1490,1491],{"class":282},"# Duck typing — no formal contract, just \"if it has the method, it works\"\n",[32,1493,1494,1496,1499],{"class":34,"line":50},[32,1495,39],{"class":38},[32,1497,1498],{"class":42}," DuckTypedShape",[32,1500,47],{"class":46},[32,1502,1503,1505,1507],{"class":34,"line":63},[32,1504,53],{"class":38},[32,1506,1217],{"class":42},[32,1508,93],{"class":46},[32,1510,1511,1513],{"class":34,"line":78},[32,1512,133],{"class":38},[32,1514,1515],{"class":56}," 42\n",[32,1517,1518],{"class":34,"line":85},[32,1519,82],{"emptyLinePlaceholder":81},[32,1521,1522,1525,1528],{"class":34,"line":96},[32,1523,1524],{"class":38},"def",[32,1526,1527],{"class":42}," print_area",[32,1529,1530],{"class":46},"(shape):\n",[32,1532,1533,1535,1538],{"class":34,"line":115},[32,1534,273],{"class":56},[32,1536,1537],{"class":46},"(shape.area())   ",[32,1539,1540],{"class":282},"# works on ANY object with an area() method, no inheritance required\n",[32,1542,1543],{"class":34,"line":120},[32,1544,82],{"emptyLinePlaceholder":81},[32,1546,1547,1550],{"class":34,"line":130},[32,1548,1549],{"class":46},"print_area(DuckTypedShape())   ",[32,1551,1552],{"class":282},"# 42\n",[32,1554,1555,1558,1560,1562,1564,1567],{"class":34,"line":164},[32,1556,1557],{"class":46},"print_area(Rectangle(",[32,1559,1435],{"class":56},[32,1561,522],{"class":46},[32,1563,1440],{"class":56},[32,1565,1566],{"class":46},"))      ",[32,1568,1569],{"class":282},"# 12\n",[291,1571,1572,1573,1575,1576,1579,1580,1583],{},"Python favors duck typing by default — ",[29,1574,1203],{}," is reached for when you specifically want to ",[295,1577,1578],{},"enforce"," a contract at instantiation time (plugin systems, framework base classes) rather than merely document one. Chapter 20 covers ",[29,1581,1582],{},"typing.Protocol",", which gives you static-analysis-time duck-typing checks without requiring inheritance at all — a third point on this spectrum.",[14,1585,1587,1588,1590],{"id":1586},"overriding-__init__-and-attribute-shadowing-in-hierarchies","Overriding ",[29,1589,437],{}," and Attribute Shadowing in Hierarchies",[19,1592,1593],{"language":21},[23,1594,1596],{"className":25,"code":1595,"language":21,"meta":27,"style":27},"class Base:\n    def __init__(self):\n        self.value = \"base\"\n\n    def show(self):\n        return self.value\n\nclass Derived(Base):\n    def __init__(self):\n        super().__init__()\n        self.value = \"derived\"    # shadows the value super().__init__() just set\n\nd = Derived()\nprint(d.show())   # \"derived\" — the LAST assignment to self.value wins, regardless of which class set it\n",[29,1597,1598,1607,1615,1627,1631,1640,1649,1653,1667,1675,1686,1700,1704,1713],{"__ignoreMap":27},[32,1599,1600,1602,1605],{"class":34,"line":35},[32,1601,39],{"class":38},[32,1603,1604],{"class":42}," Base",[32,1606,47],{"class":46},[32,1608,1609,1611,1613],{"class":34,"line":50},[32,1610,53],{"class":38},[32,1612,57],{"class":56},[32,1614,93],{"class":46},[32,1616,1617,1619,1622,1624],{"class":34,"line":63},[32,1618,66],{"class":56},[32,1620,1621],{"class":46},".value ",[32,1623,72],{"class":38},[32,1625,1626],{"class":108}," \"base\"\n",[32,1628,1629],{"class":34,"line":78},[32,1630,82],{"emptyLinePlaceholder":81},[32,1632,1633,1635,1638],{"class":34,"line":85},[32,1634,53],{"class":38},[32,1636,1637],{"class":42}," show",[32,1639,93],{"class":46},[32,1641,1642,1644,1646],{"class":34,"line":96},[32,1643,133],{"class":38},[32,1645,391],{"class":56},[32,1647,1648],{"class":46},".value\n",[32,1650,1651],{"class":34,"line":115},[32,1652,82],{"emptyLinePlaceholder":81},[32,1654,1655,1657,1660,1662,1665],{"class":34,"line":120},[32,1656,39],{"class":38},[32,1658,1659],{"class":42}," Derived",[32,1661,105],{"class":46},[32,1663,1664],{"class":42},"Base",[32,1666,182],{"class":46},[32,1668,1669,1671,1673],{"class":34,"line":130},[32,1670,53],{"class":38},[32,1672,57],{"class":56},[32,1674,93],{"class":46},[32,1676,1677,1679,1681,1683],{"class":34,"line":164},[32,1678,431],{"class":56},[32,1680,434],{"class":46},[32,1682,437],{"class":56},[32,1684,1685],{"class":46},"()\n",[32,1687,1688,1690,1692,1694,1697],{"class":34,"line":169},[32,1689,66],{"class":56},[32,1691,1621],{"class":46},[32,1693,72],{"class":38},[32,1695,1696],{"class":108}," \"derived\"",[32,1698,1699],{"class":282},"    # shadows the value super().__init__() just set\n",[32,1701,1702],{"class":34,"line":185},[32,1703,82],{"emptyLinePlaceholder":81},[32,1705,1706,1708,1710],{"class":34,"line":194},[32,1707,661],{"class":46},[32,1709,72],{"class":38},[32,1711,1712],{"class":46}," Derived()\n",[32,1714,1715,1717,1720],{"class":34,"line":202},[32,1716,537],{"class":56},[32,1718,1719],{"class":46},"(d.show())   ",[32,1721,1722],{"class":282},"# \"derived\" — the LAST assignment to self.value wins, regardless of which class set it\n",[291,1724,1725,1726,1729,1730,1733,1734,1736,1737,1740],{},"There's only ever one ",[29,1727,1728],{},"self.value"," slot per instance (absent ",[29,1731,1732],{},"__slots__"," per-class tricks) — attributes aren't namespaced by which class assigned them, so a subclass's ",[29,1735,437],{}," running after ",[29,1738,1739],{},"super().__init__()"," will always overwrite anything the parent set under the same name.",[14,1742,1744,1155,1747,1750],{"id":1743},"isinstance-and-issubclass-with-multiple-inheritance",[29,1745,1746],{},"isinstance()",[29,1748,1749],{},"issubclass()"," with Multiple Inheritance",[19,1752,1753],{"language":21},[23,1754,1756],{"className":25,"code":1755,"language":21,"meta":27,"style":27},"class Reader:\n    pass\n\nclass Writer:\n    pass\n\nclass ReadWriter(Reader, Writer):\n    pass\n\nrw = ReadWriter()\nprint(isinstance(rw, Reader))     # True\nprint(isinstance(rw, Writer))       # True\nprint(issubclass(ReadWriter, Reader))  # True\nprint(issubclass(ReadWriter, Writer))    # True\n\n# isinstance\u002Fissubclass also accept a tuple of types, checked with OR semantics\nprint(isinstance(rw, (int, str, Reader)))   # True — matches Reader\n",[29,1757,1758,1767,1771,1775,1784,1788,1792,1811,1815,1819,1829,1844,1857,1871,1884,1888,1893],{"__ignoreMap":27},[32,1759,1760,1762,1765],{"class":34,"line":35},[32,1761,39],{"class":38},[32,1763,1764],{"class":42}," Reader",[32,1766,47],{"class":46},[32,1768,1769],{"class":34,"line":50},[32,1770,652],{"class":38},[32,1772,1773],{"class":34,"line":63},[32,1774,82],{"emptyLinePlaceholder":81},[32,1776,1777,1779,1782],{"class":34,"line":78},[32,1778,39],{"class":38},[32,1780,1781],{"class":42}," Writer",[32,1783,47],{"class":46},[32,1785,1786],{"class":34,"line":85},[32,1787,652],{"class":38},[32,1789,1790],{"class":34,"line":96},[32,1791,82],{"emptyLinePlaceholder":81},[32,1793,1794,1796,1799,1801,1804,1806,1809],{"class":34,"line":115},[32,1795,39],{"class":38},[32,1797,1798],{"class":42}," ReadWriter",[32,1800,105],{"class":46},[32,1802,1803],{"class":42},"Reader",[32,1805,522],{"class":46},[32,1807,1808],{"class":42},"Writer",[32,1810,182],{"class":46},[32,1812,1813],{"class":34,"line":120},[32,1814,652],{"class":38},[32,1816,1817],{"class":34,"line":130},[32,1818,82],{"emptyLinePlaceholder":81},[32,1820,1821,1824,1826],{"class":34,"line":164},[32,1822,1823],{"class":46},"rw ",[32,1825,72],{"class":38},[32,1827,1828],{"class":46}," ReadWriter()\n",[32,1830,1831,1833,1835,1838,1841],{"class":34,"line":169},[32,1832,537],{"class":56},[32,1834,105],{"class":46},[32,1836,1837],{"class":56},"isinstance",[32,1839,1840],{"class":46},"(rw, Reader))     ",[32,1842,1843],{"class":282},"# True\n",[32,1845,1846,1848,1850,1852,1855],{"class":34,"line":185},[32,1847,537],{"class":56},[32,1849,105],{"class":46},[32,1851,1837],{"class":56},[32,1853,1854],{"class":46},"(rw, Writer))       ",[32,1856,1843],{"class":282},[32,1858,1859,1861,1863,1866,1869],{"class":34,"line":194},[32,1860,537],{"class":56},[32,1862,105],{"class":46},[32,1864,1865],{"class":56},"issubclass",[32,1867,1868],{"class":46},"(ReadWriter, Reader))  ",[32,1870,1843],{"class":282},[32,1872,1873,1875,1877,1879,1882],{"class":34,"line":202},[32,1874,537],{"class":56},[32,1876,105],{"class":46},[32,1878,1865],{"class":56},[32,1880,1881],{"class":46},"(ReadWriter, Writer))    ",[32,1883,1843],{"class":282},[32,1885,1886],{"class":34,"line":207},[32,1887,82],{"emptyLinePlaceholder":81},[32,1889,1890],{"class":34,"line":221},[32,1891,1892],{"class":282},"# isinstance\u002Fissubclass also accept a tuple of types, checked with OR semantics\n",[32,1894,1895,1897,1899,1901,1904,1907,1909,1912,1915],{"class":34,"line":230},[32,1896,537],{"class":56},[32,1898,105],{"class":46},[32,1900,1837],{"class":56},[32,1902,1903],{"class":46},"(rw, (",[32,1905,1906],{"class":56},"int",[32,1908,522],{"class":46},[32,1910,1911],{"class":56},"str",[32,1913,1914],{"class":46},", Reader)))   ",[32,1916,1917],{"class":282},"# True — matches Reader\n",[14,1919,1921],{"id":1920},"tips-tricks","💡 Tips & Tricks",[1923,1924,1925,1946,1959,1974,1987],"ul",{},[1926,1927,1928,1931,1932,1935,1936,1939,1940,1942,1943,1945],"li",{},[295,1929,1930],{},"Idiom",": prefer composition (\"has-a\") over inheritance (\"is-a\") when the relationship isn't a genuine specialization — a ",[29,1933,1934],{},"Car"," that \"has an\" ",[29,1937,1938],{},"Engine"," should hold an ",[29,1941,1938],{}," instance as an attribute, not inherit from ",[29,1944,1938],{},"; inheritance for code reuse alone often produces fragile, hard-to-follow hierarchies.",[1926,1947,1948,298,1951,1954,1955,1958],{},[295,1949,1950],{},"Debug",[29,1952,1953],{},"ClassName.__mro__"," (or ",[29,1956,1957],{},"ClassName.mro()",") prints the exact method resolution order — run it whenever a multi-inheritance call resolves to a surprising implementation instead of guessing.",[1926,1960,1961,1963,1964,1966,1967,1970,1971,1973],{},[295,1962,1930],{},": mixins should generally not define ",[29,1965,437],{}," unless they call ",[29,1968,1969],{},"super().__init__(*args, **kwargs)"," cooperatively — a mixin with a non-cooperative ",[29,1972,437],{}," silently breaks any hierarchy that places another class after it in the MRO.",[1926,1975,1976,298,1978,1980,1981,1983,1984,1986],{},[295,1977,1950],{},[29,1979,1154],{}," subclasses that seem to \"ignore\" ",[29,1982,1158],{}," are almost always missing the ",[29,1985,1203],{}," base itself, or an abstract method was overridden by a subclass with a different signature that doesn't actually match (Python doesn't check signatures, only that the name exists).",[1926,1988,1989,298,1991,1993,1994,1997,1998,2001],{},[295,1990,1930],{},[29,1992,323],{}," with no arguments (Python 3 only) works by inspecting the enclosing class and instance implicitly via ",[29,1995,1996],{},"__class__"," — the two-argument form ",[29,1999,2000],{},"super(ClassName, self)"," still works but is legacy Python 2 syntax that's rarely needed today except in some metaclass or dynamic-class-creation edge cases.",[14,2003,2005],{"id":2004},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[1923,2007,2008,2022,2033,2048,2068],{},[1926,2009,2010,2017,2018,2021],{},[295,2011,2012,2013,2016],{},"Multiple inheritance with conflicting base-class orderings raises ",[29,2014,2015],{},"TypeError: Cannot create a consistent MRO"," at class-definition time, not at instantiation"," — this is one of the few Python errors that happens purely from writing ",[29,2019,2020],{},"class Z(X, Y): pass",", before any code runs.",[1926,2023,2024,2029,2030,2032],{},[295,2025,2026,2028],{},[29,2027,323],{}," in cooperative multiple inheritance means \"next in the MRO,\" not \"my direct parent\""," — a mixin that assumes ",[29,2031,323],{}," refers to a specific class will behave differently depending on where it's placed in a consuming class's inheritance list.",[1926,2034,2035,2044,2045,2047],{},[295,2036,2037,2038,2040,2041,2043],{},"A mixin's ",[29,2039,437],{}," that doesn't call ",[29,2042,552],{}," silently breaks the initialization chain for every class placed after it in the MRO"," — those later classes' ",[29,2046,437],{}," methods simply never run, and the resulting bug (missing attributes) surfaces far from the actual cause.",[1926,2049,2050,2059,2060,2063,2064,2067],{},[295,2051,2052,2054,2055,2058],{},[29,2053,1154],{}," only checks that an abstract method ",[1135,2056,2057],{},"name"," is overridden — it does not check the overriding method's signature (parameter count, types)"," — a subclass can \"implement\" ",[29,2061,2062],{},"area(self, unit=\"m\")"," for an abstract ",[29,2065,2066],{},"area(self)"," and it will satisfy the ABC, even though callers using the base signature might behave unexpectedly.",[1926,2069,2070,2073,2074,2076,2077,2079,2080,2082],{},[295,2071,2072],{},"Instance attributes aren't scoped per class in the hierarchy — there's one namespace per instance"," — if both a base and derived ",[29,2075,437],{}," assign ",[29,2078,1728],{},", whichever runs last (usually the derived class's own assignment, after ",[29,2081,1739],{},") wins, regardless of which class \"conceptually owns\" that attribute.",[14,2084,2086],{"id":2085},"spot-the-bug","🧠 Spot the Bug",[291,2088,2089],{},"A plugin framework uses mixins to compose functionality. Some plugins mysteriously never get their setup code executed. Find the bug.",[19,2091,2092],{"language":21},[23,2093,2095],{"className":25,"code":2094,"language":21,"meta":27,"style":27},"class CachingMixin:\n    def __init__(self, *args, **kwargs):\n        self.cache = {}\n        print(\"Cache initialized\")\n\nclass LoggingMixin:\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.log = []\n        print(\"Logger initialized\")\n\nclass Plugin(LoggingMixin, CachingMixin):\n    def __init__(self, name):\n        super().__init__()\n        self.name = name\n\np = Plugin(\"analytics\")\nprint(hasattr(p, \"cache\"))\n",[29,2096,2097,2106,2126,2138,2149,2153,2161,2177,2196,2208,2219,2223,2241,2249,2259,2269,2273,2288],{"__ignoreMap":27},[32,2098,2099,2101,2104],{"class":34,"line":35},[32,2100,39],{"class":38},[32,2102,2103],{"class":42}," CachingMixin",[32,2105,47],{"class":46},[32,2107,2108,2110,2112,2115,2117,2120,2123],{"class":34,"line":50},[32,2109,53],{"class":38},[32,2111,57],{"class":56},[32,2113,2114],{"class":46},"(self, ",[32,2116,396],{"class":38},[32,2118,2119],{"class":46},"args, ",[32,2121,2122],{"class":38},"**",[32,2124,2125],{"class":46},"kwargs):\n",[32,2127,2128,2130,2133,2135],{"class":34,"line":63},[32,2129,66],{"class":56},[32,2131,2132],{"class":46},".cache ",[32,2134,72],{"class":38},[32,2136,2137],{"class":46}," {}\n",[32,2139,2140,2142,2144,2147],{"class":34,"line":78},[32,2141,997],{"class":56},[32,2143,105],{"class":46},[32,2145,2146],{"class":108},"\"Cache initialized\"",[32,2148,112],{"class":46},[32,2150,2151],{"class":34,"line":85},[32,2152,82],{"emptyLinePlaceholder":81},[32,2154,2155,2157,2159],{"class":34,"line":96},[32,2156,39],{"class":38},[32,2158,981],{"class":42},[32,2160,47],{"class":46},[32,2162,2163,2165,2167,2169,2171,2173,2175],{"class":34,"line":115},[32,2164,53],{"class":38},[32,2166,57],{"class":56},[32,2168,2114],{"class":46},[32,2170,396],{"class":38},[32,2172,2119],{"class":46},[32,2174,2122],{"class":38},[32,2176,2125],{"class":46},[32,2178,2179,2181,2183,2185,2187,2189,2191,2193],{"class":34,"line":120},[32,2180,431],{"class":56},[32,2182,434],{"class":46},[32,2184,437],{"class":56},[32,2186,105],{"class":46},[32,2188,396],{"class":38},[32,2190,2119],{"class":46},[32,2192,2122],{"class":38},[32,2194,2195],{"class":46},"kwargs)\n",[32,2197,2198,2200,2203,2205],{"class":34,"line":130},[32,2199,66],{"class":56},[32,2201,2202],{"class":46},".log ",[32,2204,72],{"class":38},[32,2206,2207],{"class":46}," []\n",[32,2209,2210,2212,2214,2217],{"class":34,"line":164},[32,2211,997],{"class":56},[32,2213,105],{"class":46},[32,2215,2216],{"class":108},"\"Logger initialized\"",[32,2218,112],{"class":46},[32,2220,2221],{"class":34,"line":169},[32,2222,82],{"emptyLinePlaceholder":81},[32,2224,2225,2227,2230,2232,2234,2236,2239],{"class":34,"line":185},[32,2226,39],{"class":38},[32,2228,2229],{"class":42}," Plugin",[32,2231,105],{"class":46},[32,2233,1069],{"class":42},[32,2235,522],{"class":46},[32,2237,2238],{"class":42},"CachingMixin",[32,2240,182],{"class":46},[32,2242,2243,2245,2247],{"class":34,"line":194},[32,2244,53],{"class":38},[32,2246,57],{"class":56},[32,2248,60],{"class":46},[32,2250,2251,2253,2255,2257],{"class":34,"line":202},[32,2252,431],{"class":56},[32,2254,434],{"class":46},[32,2256,437],{"class":56},[32,2258,1685],{"class":46},[32,2260,2261,2263,2265,2267],{"class":34,"line":207},[32,2262,66],{"class":56},[32,2264,69],{"class":46},[32,2266,72],{"class":38},[32,2268,75],{"class":46},[32,2270,2271],{"class":34,"line":221},[32,2272,82],{"emptyLinePlaceholder":81},[32,2274,2275,2278,2280,2283,2286],{"class":34,"line":230},[32,2276,2277],{"class":46},"p ",[32,2279,72],{"class":38},[32,2281,2282],{"class":46}," Plugin(",[32,2284,2285],{"class":108},"\"analytics\"",[32,2287,112],{"class":46},[32,2289,2290,2292,2294,2297,2300,2303],{"class":34,"line":238},[32,2291,537],{"class":56},[32,2293,105],{"class":46},[32,2295,2296],{"class":56},"hasattr",[32,2298,2299],{"class":46},"(p, ",[32,2301,2302],{"class":108},"\"cache\"",[32,2304,2305],{"class":46},"))\n",[2307,2308,2309,2313,2369,2395,2401,2470],"details",{},[2310,2311,2312],"summary",{},"Answer",[291,2314,2315,2316,2319,2320,2323,2324,1125,2327,2330,2331,1125,2334,2330,2337,2340,2341,2343,2344,2347,2348,2350,2351,2354,2355,2357,2358,2361,2362,2364,2365,2368],{},"Prints only ",[29,2317,2318],{},"Logger initialized"," (never ",[29,2321,2322],{},"Cache initialized","), and ",[29,2325,2326],{},"hasattr(p, \"cache\")",[29,2328,2329],{},"False",". ",[29,2332,2333],{},"Plugin.__mro__",[29,2335,2336],{},"[Plugin, LoggingMixin, CachingMixin, object]",[29,2338,2339],{},"Plugin.__init__"," calls ",[29,2342,1739],{},", which runs ",[29,2345,2346],{},"LoggingMixin.__init__","; that correctly calls ",[29,2349,1969],{},", advancing to ",[29,2352,2353],{},"CachingMixin.__init__"," — so far so good. But ",[29,2356,2353],{}," does ",[295,2359,2360],{},"not"," call ",[29,2363,1969],{}," — it just sets ",[29,2366,2367],{},"self.cache"," and returns, silently stopping the cooperative chain right there.",[291,2370,2371,2372,2374,2375,2378,2379,2381,2382,2385,2386,2388,2389,2391,2392,2394],{},"In this particular example ",[29,2373,2238],{}," is last before ",[29,2376,2377],{},"object"," so nothing further is skipped — but the real bug is structural: ",[29,2380,2238],{}," breaks the contract of cooperative multiple inheritance. Swap the class order to ",[29,2383,2384],{},"Plugin(CachingMixin, LoggingMixin)"," and now ",[29,2387,2346],{}," (which does the right thing) never runs at all, because ",[29,2390,2353],{}," runs first and never calls ",[29,2393,323],{},".",[291,2396,2397,2398,2400],{},"The fix is to make every mixin in a cooperative hierarchy always call ",[29,2399,1969],{},", even if it appears to be \"the last one\":",[19,2402,2403],{"language":21},[23,2404,2406],{"className":25,"code":2405,"language":21,"meta":27,"style":27},"class CachingMixin:\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.cache = {}\n        print(\"Cache initialized\")\n",[29,2407,2408,2416,2432,2450,2460],{"__ignoreMap":27},[32,2409,2410,2412,2414],{"class":34,"line":35},[32,2411,39],{"class":38},[32,2413,2103],{"class":42},[32,2415,47],{"class":46},[32,2417,2418,2420,2422,2424,2426,2428,2430],{"class":34,"line":50},[32,2419,53],{"class":38},[32,2421,57],{"class":56},[32,2423,2114],{"class":46},[32,2425,396],{"class":38},[32,2427,2119],{"class":46},[32,2429,2122],{"class":38},[32,2431,2125],{"class":46},[32,2433,2434,2436,2438,2440,2442,2444,2446,2448],{"class":34,"line":63},[32,2435,431],{"class":56},[32,2437,434],{"class":46},[32,2439,437],{"class":56},[32,2441,105],{"class":46},[32,2443,396],{"class":38},[32,2445,2119],{"class":46},[32,2447,2122],{"class":38},[32,2449,2195],{"class":46},[32,2451,2452,2454,2456,2458],{"class":34,"line":78},[32,2453,66],{"class":56},[32,2455,2132],{"class":46},[32,2457,72],{"class":38},[32,2459,2137],{"class":46},[32,2461,2462,2464,2466,2468],{"class":34,"line":85},[32,2463,997],{"class":56},[32,2465,105],{"class":46},[32,2467,2146],{"class":108},[32,2469,112],{"class":46},[291,2471,2472,2475,2476,2478],{},[295,2473,2474],{},"The lesson",": every class participating in cooperative multiple inheritance must call ",[29,2477,1969],{},", regardless of where you assume it sits in the MRO — assuming your class is \"the base\" is exactly the assumption that breaks when someone reorders the inheritance list later.",[14,2480,2482],{"id":2481},"key-takeaways","Key Takeaways",[1923,2484,2485,2488,2497,2504,2510,2521],{},[1926,2486,2487],{},"Polymorphism in Python needs no special syntax — any object with the right method name works wherever that method is called, resolved dynamically at call time based on the object's actual type.",[1926,2489,2490,2492,2493,2496],{},[29,2491,323],{}," delegates to the ",[1135,2494,2495],{},"next class in the MRO",", not necessarily the immediate parent — this distinction only matters (and matters a lot) once multiple inheritance is involved.",[1926,2498,2499,2500,2503],{},"Python computes a single, well-defined Method Resolution Order via C3 linearization; conflicting orderings raise ",[29,2501,2502],{},"TypeError"," at class-definition time rather than picking an arbitrary order.",[1926,2505,2506,2507,2509],{},"Cooperative multiple inheritance requires every class in the chain — including mixins — to call ",[29,2508,1969],{},", or later classes in the MRO silently never get initialized.",[1926,2511,2512,2514,2515,2517,2518,2520],{},[29,2513,1154],{}," + ",[29,2516,1158],{}," enforces a contract at instantiation time, failing fast; the ",[29,2519,1459],{},"-in-a-plain-method pattern only fails when that specific method is eventually called.",[1926,2522,2523],{},"Prefer composition over inheritance for \"has-a\" relationships — reach for inheritance (and especially multiple inheritance) only for genuine \"is-a\" specialization or well-designed cooperative mixins.",[2525,2526,2527],"style",{},"html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-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 .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}",{"title":27,"searchDepth":50,"depth":50,"links":2529},[2530,2531,2533,2534,2537,2539,2543,2545,2547,2548,2549,2550],{"id":16,"depth":50,"text":17},{"id":320,"depth":50,"text":2532},"super() — Calling the Parent's Implementation",{"id":559,"depth":50,"text":560},{"id":686,"depth":50,"text":687,"children":2535},[2536],{"id":877,"depth":63,"text":878},{"id":959,"depth":50,"text":2538},"Cooperative Multiple Inheritance with super()",{"id":1148,"depth":50,"text":1149,"children":2540},[2541],{"id":1473,"depth":63,"text":2542},"ABC vs duck typing vs Protocol",{"id":1586,"depth":50,"text":2544},"Overriding __init__ and Attribute Shadowing in Hierarchies",{"id":1743,"depth":50,"text":2546},"isinstance() and issubclass() with Multiple Inheritance",{"id":1920,"depth":50,"text":1921},{"id":2004,"depth":50,"text":2005},{"id":2085,"depth":50,"text":2086},{"id":2481,"depth":50,"text":2482},"md",{},"\u002Fpython\u002F13-inheritance-and-polymorphism",{"title":5,"description":27},"python\u002F13-inheritance-and-polymorphism","LUyzkvpMls2Jk0rp9iQ9TKCpL-GNjvIfgOIEANc5axI",1789924651518]