[{"data":1,"prerenderedAt":2575},["ShallowReactive",2],{"page-\u002Fpython\u002F12-classes-and-objects":3},{"id":4,"title":5,"body":6,"description":651,"extension":2569,"meta":2570,"navigation":34,"path":2571,"seo":2572,"stem":2573,"__hash__":2574},"content\u002Fpython\u002F12-classes-and-objects.md","12 — Classes & Objects",{"type":7,"value":8,"toc":2533},"minimark",[9,13,100,216,293,408,470,536,636,838,986,1240,1405,1617,1785,2000,2090,2179,2274,2353,2423,2529],[10,11,5],"h1",{"id":12},"_12-classes-objects",[14,15,16,21,69],"question-wrapper",{},[17,18,20],"h3",{"id":19},"q1-what-is-the-difference-between-a-class-attribute-and-an-instance-attribute","Q1. What is the difference between a class attribute and an instance attribute?",[22,23,26,45,51,60],"ul",{"className":24},[25],"contains-task-list",[27,28,31,36,37,41,42],"li",{"className":29},[30],"task-list-item",[32,33],"input",{"disabled":34,"type":35},true,"checkbox"," A class attribute is defined in the class body and shared by all instances unless overridden; an instance attribute is set per-object, usually via ",[38,39,40],"code",{},"self"," in ",[38,43,44],{},"__init__",[27,46,48,50],{"className":47},[30],[32,49],{"disabled":34,"type":35}," A class attribute can only hold integers or strings; an instance attribute can hold any type",[27,52,54,56,57],{"className":53},[30],[32,55],{"disabled":34,"type":35}," There is no functional difference — both are stored identically in every object's ",[38,58,59],{},"__dict__",[27,61,63,65,66,68],{"className":62},[30],[32,64],{"disabled":34,"type":35}," A class attribute is only visible inside ",[38,67,44],{},", while an instance attribute is visible everywhere",[70,71,72,76,88],"details",{},[73,74,75],"summary",{},"Show Answer",[77,78,79,83,84,41,86],"p",{},[80,81,82],"strong",{},"Answer:"," A — A class attribute is defined in the class body and shared by all instances unless overridden; an instance attribute is set per-object, usually via ",[38,85,40],{},[38,87,44],{},[77,89,90,93,94,96,97,99],{},[80,91,92],{},"Explanation:"," Class attributes live in the class's own ",[38,95,59],{}," and are looked up via the class if an instance doesn't have its own copy. Instance attributes live in each object's own ",[38,98,59],{}," and take precedence over a class attribute of the same name. They are stored differently, not identically (option C) — this distinction is exactly what causes the shared-mutable-default bug covered later in this quiz.",[14,101,102,113,167],{},[17,103,105,106,108,109,112],{"id":104},"q2-what-is-the-difference-between-__init__-and-__new__","Q2. What is the difference between ",[38,107,44],{}," and ",[38,110,111],{},"__new__","?",[22,114,116,139,145,156],{"className":115},[25],[27,117,119,121,122,124,125,129,130,121,132,135,136],{"className":118},[30],[32,120],{"disabled":34,"type":35}," ",[38,123,111],{}," is responsible for ",[126,127,128],"em",{},"creating"," and returning a new instance (called before the object exists); ",[38,131,44],{},[126,133,134],{},"initializes"," an already-created instance and returns ",[38,137,138],{},"None",[27,140,142,144],{"className":141},[30],[32,143],{"disabled":34,"type":35}," They are aliases for the same method; Python calls whichever one is defined",[27,146,148,121,150,152,153,155],{"className":147},[30],[32,149],{"disabled":34,"type":35},[38,151,44],{}," creates the object; ",[38,154,111],{}," only runs for subclasses of built-in immutable types",[27,157,159,121,161,163,164,166],{"className":158},[30],[32,160],{"disabled":34,"type":35},[38,162,111],{}," runs after ",[38,165,44],{}," to finalize attribute defaults",[70,168,169,171,186],{},[73,170,75],{},[77,172,173,175,176,124,178,129,180,121,182,135,184],{},[80,174,82],{}," A — ",[38,177,111],{},[126,179,128],{},[38,181,44],{},[126,183,134],{},[38,185,138],{},[77,187,188,121,190,192,193,196,197,199,200,203,204,206,207,209,210,212,213,215],{},[80,189,92],{},[38,191,111],{}," is a static method (implicitly) that allocates and returns the object — it's what actually calls ",[38,194,195],{},"object.__new__(cls)"," under the hood. Only after ",[38,198,111],{}," returns an instance of ",[38,201,202],{},"cls"," (or a subclass) does Python call ",[38,205,44],{}," on it to set up its initial state. Most classes never override ",[38,208,111],{}," because ",[38,211,44],{}," is sufficient; overriding ",[38,214,111],{}," matters for immutable types and patterns like singletons, covered later in this quiz.",[14,217,218,225,264],{},[17,219,221,222,224],{"id":220},"q3-is-self-a-reserved-keyword-in-python","Q3. Is ",[38,223,40],{}," a reserved keyword in Python?",[22,226,228,234,243,255],{"className":227},[25],[27,229,231,233],{"className":230},[30],[32,232],{"disabled":34,"type":35}," No — it's just a strong convention; any valid identifier could be used for the first parameter of an instance method",[27,235,237,239,240,242],{"className":236},[30],[32,238],{"disabled":34,"type":35}," Yes — Python's parser specifically recognizes ",[38,241,40],{}," and treats it as the instance reference",[27,244,246,248,249,251,252],{"className":245},[30],[32,247],{"disabled":34,"type":35}," No, but using anything other than ",[38,250,40],{}," causes a ",[38,253,254],{},"SyntaxError",[27,256,258,260,261,263],{"className":257},[30],[32,259],{"disabled":34,"type":35}," Yes, but only inside ",[38,262,44],{},"; other methods may use any name",[70,265,266,268,273],{},[73,267,75],{},[77,269,270,272],{},[80,271,82],{}," A — No — it's just a strong convention; any valid identifier could be used for the first parameter of an instance method",[77,274,275,277,278,281,282,285,286,288,289,292],{},[80,276,92],{}," When you call ",[38,279,280],{},"obj.method(args)",", Python translates it to ",[38,283,284],{},"ClassName.method(obj, args)"," — the first positional parameter always receives the instance, regardless of what it's named. ",[38,287,40],{}," is PEP 8 convention (and nearly universal in the ecosystem), but writing ",[38,290,291],{},"def method(this, ...)"," is perfectly legal and behaves identically. It only becomes a real problem when it breaks readability or violates a linter's expectations, not because Python enforces the name.",[14,294,295,309,356],{},[17,296,298,299,302,303,305,306,112],{"id":297},"q4-in-class-foo-def-barself-x-what-gets-passed-as-self-when-you-call-foobar5","Q4. In ",[38,300,301],{},"class Foo: def bar(self, x): ...",", what gets passed as ",[38,304,40],{}," when you call ",[38,307,308],{},"Foo().bar(5)",[22,310,312,326,334,342],{"className":311},[25],[27,313,315,317,318,321,322,325],{"className":314},[30],[32,316],{"disabled":34,"type":35}," The ",[38,319,320],{},"Foo"," instance created by ",[38,323,324],{},"Foo()",", automatically supplied by Python's attribute-lookup\u002Fbinding machinery",[27,327,329,317,331,333],{"className":328},[30],[32,330],{"disabled":34,"type":35},[38,332,320],{}," class itself",[27,335,337,121,339,341],{"className":336},[30],[32,338],{"disabled":34,"type":35},[38,340,138],{},", unless explicitly passed",[27,343,345,347,348,351,352,355],{"className":344},[30],[32,346],{"disabled":34,"type":35}," The integer ",[38,349,350],{},"5",", with ",[38,353,354],{},"x"," left unbound",[70,357,358,360,369],{},[73,359,75],{},[77,361,362,364,365,321,367,325],{},[80,363,82],{}," A — The ",[38,366,320],{},[38,368,324],{},[77,370,371,373,374,377,378,381,382,385,386,389,390,392,393,395,396,398,399,401,402,404,405,407],{},[80,372,92],{}," Accessing ",[38,375,376],{},"instance.bar"," produces a ",[126,379,380],{},"bound method"," — a callable that has already captured the instance as its first argument. ",[38,383,384],{},"bar(5)"," on that bound method is equivalent to ",[38,387,388],{},"Foo.bar(instance, 5)",", so ",[38,391,40],{}," gets the instance and ",[38,394,354],{}," gets ",[38,397,350],{},". Confusing ",[38,400,40],{}," with the class itself (option B) is what ",[38,403,202],{}," in classmethods is for, not ",[38,406,40],{},".",[14,409,410,418,448],{},[17,411,413,414,417],{"id":412},"q5-what-distinguishes-a-classmethod-from-a-regular-instance-method","Q5. What distinguishes a ",[38,415,416],{},"@classmethod"," from a regular instance method?",[22,419,421,430,436,442],{"className":420},[25],[27,422,424,426,427,429],{"className":423},[30],[32,425],{"disabled":34,"type":35}," It receives the class (",[38,428,202],{},") as its first argument instead of the instance, and can be called on the class itself without an instance",[27,431,433,435],{"className":432},[30],[32,434],{"disabled":34,"type":35}," It cannot access any attributes at all",[27,437,439,441],{"className":438},[30],[32,440],{"disabled":34,"type":35}," It automatically becomes private (name-mangled)",[27,443,445,447],{"className":444},[30],[32,446],{"disabled":34,"type":35}," It runs at import time instead of when explicitly called",[70,449,450,452,459],{},[73,451,75],{},[77,453,454,456,457,429],{},[80,455,82],{}," A — It receives the class (",[38,458,202],{},[77,460,461,121,463,465,466,469],{},[80,462,92],{},[38,464,416],{}," binds the first parameter to the class the method was accessed through (which, importantly, is the ",[126,467,468],{},"actual"," subclass when called via a subclass — not necessarily the class where the method was defined). This makes classmethods the idiomatic tool for alternative constructors, covered later in this quiz.",[14,471,472,480,515],{},[17,473,475,476,479],{"id":474},"q6-what-distinguishes-a-staticmethod-from-both-instance-and-class-methods","Q6. What distinguishes a ",[38,477,478],{},"@staticmethod"," from both instance and class methods?",[22,481,483,489,500,506],{"className":482},[25],[27,484,486,488],{"className":485},[30],[32,487],{"disabled":34,"type":35}," It receives no implicit first argument at all — it behaves like a plain function that simply happens to live in the class's namespace",[27,490,492,494,495,497,498],{"className":491},[30],[32,493],{"disabled":34,"type":35}," It receives ",[38,496,40],{}," but not ",[38,499,202],{},[27,501,503,505],{"className":502},[30],[32,504],{"disabled":34,"type":35}," It can only be called before any instance of the class is created",[27,507,509,511,512],{"className":508},[30],[32,510],{"disabled":34,"type":35}," It is automatically cached like ",[38,513,514],{},"functools.lru_cache",[70,516,517,519,524],{},[73,518,75],{},[77,520,521,523],{},[80,522,82],{}," A — It receives no implicit first argument at all — it behaves like a plain function that simply happens to live in the class's namespace",[77,525,526,121,528,530,531,533,534,407],{},[80,527,92],{},[38,529,478],{}," opts a method out of both instance-binding and class-binding; it gets exactly the arguments you pass it, nothing more. It's used purely for namespacing a helper function under a class for organizational reasons, not because it needs ",[38,532,40],{}," or ",[38,535,202],{},[14,537,538,550,592],{},[17,539,541,542,108,544,546,547,112],{"id":540},"q7-in-what-order-does-python-call-__new__-and-__init__-when-you-write-foox-y","Q7. In what order does Python call ",[38,543,111],{},[38,545,44],{}," when you write ",[38,548,549],{},"Foo(x, y)",[22,551,553,566,577,586],{"className":552},[25],[27,554,556,121,558,561,562,565],{"className":555},[30],[32,557],{"disabled":34,"type":35},[38,559,560],{},"Foo.__new__(Foo, x, y)"," is called first to create the instance; then ",[38,563,564],{},"Foo.__init__(instance, x, y)"," is called on the result",[27,567,569,121,571,573,574,576],{"className":568},[30],[32,570],{"disabled":34,"type":35},[38,572,44],{}," runs first to set defaults, then ",[38,575,111],{}," finalizes the object",[27,578,580,582,583],{"className":579},[30],[32,581],{"disabled":34,"type":35}," Only one of them runs, depending on whether the class defines ",[38,584,585],{},"__slots__",[27,587,589,591],{"className":588},[30],[32,590],{"disabled":34,"type":35}," They run concurrently in separate threads",[70,593,594,596,604],{},[73,595,75],{},[77,597,598,175,600,561,602,565],{},[80,599,82],{},[38,601,560],{},[38,603,564],{},[77,605,606,608,609,612,613,616,617,619,620,623,624,626,627,629,630,632,633,635],{},[80,607,92],{}," Object construction is a two-step protocol: ",[38,610,611],{},"type.__call__"," (invoked implicitly by ",[38,614,615],{},"Foo(...)",") first calls ",[38,618,111],{}," to obtain an instance, then — ",[126,621,622],{},"only if"," the returned object is an instance of ",[38,625,320],{}," (or a subclass) — calls ",[38,628,44],{}," on it with the same arguments. This conditional call is itself a gotcha covered later: if ",[38,631,111],{}," returns something of an unrelated type, ",[38,634,44],{}," is skipped entirely.",[14,637,639,643,759,794],{"language":638},"python",[17,640,642],{"id":641},"q8-what-does-this-print","Q8. What does this print?",[644,645,646],"code-wrapper",{"language":638},[647,648,652],"pre",{"className":649,"code":650,"language":638,"meta":651,"style":651},"language-python shiki shiki-themes github-light github-dark","class ShoppingCart:\n    items = []\n\n    def add(self, item):\n        self.items.append(item)\n\ncart_a = ShoppingCart()\ncart_b = ShoppingCart()\ncart_a.add(\"apple\")\nprint(cart_b.items)\n","",[38,653,654,671,683,689,701,711,716,727,737,750],{"__ignoreMap":651},[655,656,659,663,667],"span",{"class":657,"line":658},"line",1,[655,660,662],{"class":661},"svdQ7","class",[655,664,666],{"class":665},"sIsaT"," ShoppingCart",[655,668,670],{"class":669},"ssxIu",":\n",[655,672,674,677,680],{"class":657,"line":673},2,[655,675,676],{"class":669},"    items ",[655,678,679],{"class":661},"=",[655,681,682],{"class":669}," []\n",[655,684,686],{"class":657,"line":685},3,[655,687,688],{"emptyLinePlaceholder":34},"\n",[655,690,692,695,698],{"class":657,"line":691},4,[655,693,694],{"class":661},"    def",[655,696,697],{"class":665}," add",[655,699,700],{"class":669},"(self, item):\n",[655,702,704,708],{"class":657,"line":703},5,[655,705,707],{"class":706},"snvgF","        self",[655,709,710],{"class":669},".items.append(item)\n",[655,712,714],{"class":657,"line":713},6,[655,715,688],{"emptyLinePlaceholder":34},[655,717,719,722,724],{"class":657,"line":718},7,[655,720,721],{"class":669},"cart_a ",[655,723,679],{"class":661},[655,725,726],{"class":669}," ShoppingCart()\n",[655,728,730,733,735],{"class":657,"line":729},8,[655,731,732],{"class":669},"cart_b ",[655,734,679],{"class":661},[655,736,726],{"class":669},[655,738,740,743,747],{"class":657,"line":739},9,[655,741,742],{"class":669},"cart_a.add(",[655,744,746],{"class":745},"sJ6F3","\"apple\"",[655,748,749],{"class":669},")\n",[655,751,753,756],{"class":657,"line":752},10,[655,754,755],{"class":706},"print",[655,757,758],{"class":669},"(cart_b.items)\n",[22,760,762,770,778,786],{"className":761},[25],[27,763,765,121,767],{"className":764},[30],[32,766],{"disabled":34,"type":35},[38,768,769],{},"['apple']",[27,771,773,121,775],{"className":772},[30],[32,774],{"disabled":34,"type":35},[38,776,777],{},"[]",[27,779,781,121,783],{"className":780},[30],[32,782],{"disabled":34,"type":35},[38,784,785],{},"AttributeError: 'ShoppingCart' object has no attribute 'items'",[27,787,789,121,791],{"className":788},[30],[32,790],{"disabled":34,"type":35},[38,792,793],{},"['apple', 'apple']",[70,795,796,798,804],{},[73,797,75],{},[77,799,800,175,802],{},[80,801,82],{},[38,803,769],{},[77,805,806,121,808,811,812,815,816,818,819,822,823,826,827,108,830,833,834,837],{},[80,807,92],{},[80,809,810],{},"Debug"," — ",[38,813,814],{},"items = []"," is a ",[126,817,662],{}," attribute, created once when the class body executes, not a fresh list per instance. ",[38,820,821],{},"self.items.append(...)"," doesn't reassign ",[38,824,825],{},"self.items"," (which would create a new instance attribute); it mutates the single list object in place, and since both ",[38,828,829],{},"cart_a",[38,831,832],{},"cart_b"," look up ",[38,835,836],{},"items"," through the same class (neither has its own instance attribute), they see the same shared list. This is one of Python's most infamous shared-mutable-state footguns — structurally identical to the mutable-default-argument trap.",[14,839,840,851,905,957],{"language":638},[17,841,843,844,847,848,850],{"id":842},"q9-which-fix-correctly-gives-each-shoppingcart-instance-its-own-independent-items-list","Q9. Which fix correctly gives each ",[38,845,846],{},"ShoppingCart"," instance its own independent ",[38,849,836],{}," list?",[644,852,853],{"language":638},[647,854,856],{"className":649,"code":855,"language":638,"meta":651,"style":651},"class ShoppingCart:\n    def __init__(self):\n        self.items = []\n\n    def add(self, item):\n        self.items.append(item)\n",[38,857,858,866,876,887,891,899],{"__ignoreMap":651},[655,859,860,862,864],{"class":657,"line":658},[655,861,662],{"class":661},[655,863,666],{"class":665},[655,865,670],{"class":669},[655,867,868,870,873],{"class":657,"line":673},[655,869,694],{"class":661},[655,871,872],{"class":706}," __init__",[655,874,875],{"class":669},"(self):\n",[655,877,878,880,883,885],{"class":657,"line":685},[655,879,707],{"class":706},[655,881,882],{"class":669},".items ",[655,884,679],{"class":661},[655,886,682],{"class":669},[655,888,889],{"class":657,"line":691},[655,890,688],{"emptyLinePlaceholder":34},[655,892,893,895,897],{"class":657,"line":703},[655,894,694],{"class":661},[655,896,697],{"class":665},[655,898,700],{"class":669},[655,900,901,903],{"class":657,"line":713},[655,902,707],{"class":706},[655,904,710],{"class":669},[22,906,908,924,934,947],{"className":907},[25],[27,909,911,913,914,917,918,920,921,923],{"className":910},[30],[32,912],{"disabled":34,"type":35}," This version — assigning ",[38,915,916],{},"self.items = []"," inside ",[38,919,44],{}," creates a brand-new list object as an instance attribute for every ",[38,922,846],{}," created",[27,925,927,929,930,933],{"className":926},[30],[32,928],{"disabled":34,"type":35}," Declaring ",[38,931,932],{},"items: list = []"," as a type-annotated class attribute instead",[27,935,937,939,940,917,943,946],{"className":936},[30],[32,938],{"disabled":34,"type":35}," Using ",[38,941,942],{},"self.items = list(ShoppingCart.items)",[38,944,945],{},"add"," before every append",[27,948,950,952,953,956],{"className":949},[30],[32,951],{"disabled":34,"type":35}," The original code was already correct; the bug in Q8 was caused by calling ",[38,954,955],{},".add()"," twice",[70,958,959,961,972],{},[73,960,75],{},[77,962,963,965,966,917,968,920,970,923],{},[80,964,82],{}," A — This version — assigning ",[38,967,916],{},[38,969,44],{},[38,971,846],{},[77,973,974,976,977,979,980,982,983,985],{},[80,975,92],{}," Because ",[38,978,44],{}," runs once per instance, ",[38,981,916],{}," executes fresh for every object, producing a distinct list each time and shadowing any class-level attribute of the same name. Adding a type annotation (option B) doesn't change the fundamental problem — it's still one shared list evaluated once at class-definition time. This is the general fix pattern: any mutable default (list, dict, set) should be created inside ",[38,984,44],{},", never as a bare class attribute meant to be per-instance state.",[14,987,988,992,1119,1190],{"language":638},[17,989,991],{"id":990},"q10-what-happens-here","Q10. What happens here?",[644,993,994],{"language":638},[647,995,997],{"className":649,"code":996,"language":638,"meta":651,"style":651},"class Singleton:\n    _instance = None\n\n    def __new__(cls):\n        return cls._instance or object.__new__(cls)\n\n    def __init__(self):\n        print(\"init ran\")\n\ns1 = Singleton()\nSingleton._instance = s1\ns2 = Singleton()\n",[38,998,999,1008,1018,1022,1032,1060,1064,1072,1084,1088,1098,1109],{"__ignoreMap":651},[655,1000,1001,1003,1006],{"class":657,"line":658},[655,1002,662],{"class":661},[655,1004,1005],{"class":665}," Singleton",[655,1007,670],{"class":669},[655,1009,1010,1013,1015],{"class":657,"line":673},[655,1011,1012],{"class":669},"    _instance ",[655,1014,679],{"class":661},[655,1016,1017],{"class":706}," None\n",[655,1019,1020],{"class":657,"line":685},[655,1021,688],{"emptyLinePlaceholder":34},[655,1023,1024,1026,1029],{"class":657,"line":691},[655,1025,694],{"class":661},[655,1027,1028],{"class":706}," __new__",[655,1030,1031],{"class":669},"(cls):\n",[655,1033,1034,1037,1040,1043,1046,1049,1051,1053,1056,1058],{"class":657,"line":703},[655,1035,1036],{"class":661},"        return",[655,1038,1039],{"class":706}," cls",[655,1041,1042],{"class":669},"._instance ",[655,1044,1045],{"class":661},"or",[655,1047,1048],{"class":706}," object",[655,1050,407],{"class":669},[655,1052,111],{"class":706},[655,1054,1055],{"class":669},"(",[655,1057,202],{"class":706},[655,1059,749],{"class":669},[655,1061,1062],{"class":657,"line":713},[655,1063,688],{"emptyLinePlaceholder":34},[655,1065,1066,1068,1070],{"class":657,"line":718},[655,1067,694],{"class":661},[655,1069,872],{"class":706},[655,1071,875],{"class":669},[655,1073,1074,1077,1079,1082],{"class":657,"line":729},[655,1075,1076],{"class":706},"        print",[655,1078,1055],{"class":669},[655,1080,1081],{"class":745},"\"init ran\"",[655,1083,749],{"class":669},[655,1085,1086],{"class":657,"line":739},[655,1087,688],{"emptyLinePlaceholder":34},[655,1089,1090,1093,1095],{"class":657,"line":752},[655,1091,1092],{"class":669},"s1 ",[655,1094,679],{"class":661},[655,1096,1097],{"class":669}," Singleton()\n",[655,1099,1101,1104,1106],{"class":657,"line":1100},11,[655,1102,1103],{"class":669},"Singleton._instance ",[655,1105,679],{"class":661},[655,1107,1108],{"class":669}," s1\n",[655,1110,1112,1115,1117],{"class":657,"line":1111},12,[655,1113,1114],{"class":669},"s2 ",[655,1116,679],{"class":661},[655,1118,1097],{"class":669},[22,1120,1122,1152,1163,1175],{"className":1121},[25],[27,1123,1125,121,1127,1129,1130,1133,1134,1137,1138,1141,1142,1144,1145,1147,1148,1151],{"className":1124},[30],[32,1126],{"disabled":34,"type":35},[38,1128,1081],{}," prints twice — once for ",[38,1131,1132],{},"s1",", and again for ",[38,1135,1136],{},"s2",", even though ",[38,1139,1140],{},"s2 is s1",", because ",[38,1143,44],{}," is called whenever ",[38,1146,111],{}," returns an instance of the class, regardless of whether it's a ",[126,1149,1150],{},"reused"," instance",[27,1153,1155,121,1157,1159,1160,1162],{"className":1154},[30],[32,1156],{"disabled":34,"type":35},[38,1158,1081],{}," prints once, because Python detects that ",[38,1161,1140],{}," and skips re-initialization",[27,1164,1166,121,1168,1171,1172,1174],{"className":1165},[30],[32,1167],{"disabled":34,"type":35},[38,1169,1170],{},"TypeError"," is raised because ",[38,1173,111],{}," doesn't accept extra arguments",[27,1176,1178,121,1180,1182,1183,1186,1187,1189],{"className":1177},[30],[32,1179],{"disabled":34,"type":35},[38,1181,1081],{}," never prints because ",[38,1184,1185],{},"_instance"," is ",[38,1188,138],{}," initially",[70,1191,1192,1194,1212],{},[73,1193,75],{},[77,1195,1196,175,1198,1129,1200,1133,1202,1137,1204,1141,1206,1144,1208,1147,1210,1151],{},[80,1197,82],{},[38,1199,1081],{},[38,1201,1132],{},[38,1203,1136],{},[38,1205,1140],{},[38,1207,44],{},[38,1209,111],{},[126,1211,1150],{},[77,1213,1214,121,1216,1218,1219,199,1221,1223,1224,1226,1227,1229,1230,1232,1233,1235,1236,1239],{},[80,1215,92],{},[80,1217,810],{}," — Python's rule is purely type-based: if ",[38,1220,111],{},[38,1222,202],{}," (or a subclass), ",[38,1225,44],{}," runs on it — Python does not check whether the object is \"new\" versus recycled. This is exactly why naive singleton implementations that only override ",[38,1228,111],{}," still re-run ",[38,1231,44],{}," on every \"construction,\" silently resetting state each time unless ",[38,1234,44],{}," is guarded (e.g., with an ",[38,1237,1238],{},"if self._initialized:"," check).",[14,1241,1242,1246,1312,1347],{"language":638},[17,1243,1245],{"id":1244},"q11-what-does-this-print","Q11. What does this print?",[644,1247,1248],{"language":638},[647,1249,1251],{"className":649,"code":1250,"language":638,"meta":651,"style":651},"class Config:\n    debug = False\n\nc1 = Config()\nc2 = Config()\nc1.debug = True\nprint(c1.debug, c2.debug, Config.debug)\n",[38,1252,1253,1262,1272,1276,1286,1295,1305],{"__ignoreMap":651},[655,1254,1255,1257,1260],{"class":657,"line":658},[655,1256,662],{"class":661},[655,1258,1259],{"class":665}," Config",[655,1261,670],{"class":669},[655,1263,1264,1267,1269],{"class":657,"line":673},[655,1265,1266],{"class":669},"    debug ",[655,1268,679],{"class":661},[655,1270,1271],{"class":706}," False\n",[655,1273,1274],{"class":657,"line":685},[655,1275,688],{"emptyLinePlaceholder":34},[655,1277,1278,1281,1283],{"class":657,"line":691},[655,1279,1280],{"class":669},"c1 ",[655,1282,679],{"class":661},[655,1284,1285],{"class":669}," Config()\n",[655,1287,1288,1291,1293],{"class":657,"line":703},[655,1289,1290],{"class":669},"c2 ",[655,1292,679],{"class":661},[655,1294,1285],{"class":669},[655,1296,1297,1300,1302],{"class":657,"line":713},[655,1298,1299],{"class":669},"c1.debug ",[655,1301,679],{"class":661},[655,1303,1304],{"class":706}," True\n",[655,1306,1307,1309],{"class":657,"line":718},[655,1308,755],{"class":706},[655,1310,1311],{"class":669},"(c1.debug, c2.debug, Config.debug)\n",[22,1313,1315,1323,1331,1339],{"className":1314},[25],[27,1316,1318,121,1320],{"className":1317},[30],[32,1319],{"disabled":34,"type":35},[38,1321,1322],{},"True False False",[27,1324,1326,121,1328],{"className":1325},[30],[32,1327],{"disabled":34,"type":35},[38,1329,1330],{},"True True True",[27,1332,1334,121,1336],{"className":1333},[30],[32,1335],{"disabled":34,"type":35},[38,1337,1338],{},"False False False",[27,1340,1342,121,1344],{"className":1341},[30],[32,1343],{"disabled":34,"type":35},[38,1345,1346],{},"AttributeError",[70,1348,1349,1351,1357],{},[73,1350,75],{},[77,1352,1353,175,1355],{},[80,1354,82],{},[38,1356,1322],{},[77,1358,1359,121,1361,811,1363,1366,1367,1370,1371,1374,1375,1378,1379,1382,1383,1386,1387,1389,1390,108,1393,1396,1397,1400,1401,1404],{},[80,1360,92],{},[80,1362,810],{},[38,1364,1365],{},"c1.debug = True"," does ",[126,1368,1369],{},"not"," mutate the class attribute; because ",[38,1372,1373],{},"bool"," is immutable, this assignment creates a brand-new ",[126,1376,1377],{},"instance"," attribute on ",[38,1380,1381],{},"c1"," that shadows ",[38,1384,1385],{},"Config.debug"," when accessed through ",[38,1388,1381],{},". ",[38,1391,1392],{},"c2",[38,1394,1395],{},"Config"," itself are unaffected, since they still resolve ",[38,1398,1399],{},"debug"," via the class attribute. This is the crucial contrast with Q8: reassigning a name creates an instance attribute (isolated), while mutating a shared mutable object in place (like ",[38,1402,1403],{},".append()",") affects everyone who shares that object — the same operation (\"set an attribute on self\") behaves completely differently depending on whether you assign or mutate.",[14,1406,1407,1411,1533,1586],{"language":638},[17,1408,1410],{"id":1409},"q12-is-this-class-valid-and-does-it-behave-normally","Q12. Is this class valid, and does it behave normally?",[644,1412,1413],{"language":638},[647,1414,1416],{"className":649,"code":1415,"language":638,"meta":651,"style":651},"class Point:\n    def __init__(this, x, y):\n        this.x = x\n        this.y = y\n\n    def distance_from_origin(this):\n        return (this.x ** 2 + this.y ** 2) ** 0.5\n\np = Point(3, 4)\nprint(p.distance_from_origin())\n",[38,1417,1418,1427,1436,1446,1456,1460,1470,1501,1505,1526],{"__ignoreMap":651},[655,1419,1420,1422,1425],{"class":657,"line":658},[655,1421,662],{"class":661},[655,1423,1424],{"class":665}," Point",[655,1426,670],{"class":669},[655,1428,1429,1431,1433],{"class":657,"line":673},[655,1430,694],{"class":661},[655,1432,872],{"class":706},[655,1434,1435],{"class":669},"(this, x, y):\n",[655,1437,1438,1441,1443],{"class":657,"line":685},[655,1439,1440],{"class":669},"        this.x ",[655,1442,679],{"class":661},[655,1444,1445],{"class":669}," x\n",[655,1447,1448,1451,1453],{"class":657,"line":691},[655,1449,1450],{"class":669},"        this.y ",[655,1452,679],{"class":661},[655,1454,1455],{"class":669}," y\n",[655,1457,1458],{"class":657,"line":703},[655,1459,688],{"emptyLinePlaceholder":34},[655,1461,1462,1464,1467],{"class":657,"line":713},[655,1463,694],{"class":661},[655,1465,1466],{"class":665}," distance_from_origin",[655,1468,1469],{"class":669},"(this):\n",[655,1471,1472,1474,1477,1480,1483,1486,1489,1491,1493,1496,1498],{"class":657,"line":718},[655,1473,1036],{"class":661},[655,1475,1476],{"class":669}," (this.x ",[655,1478,1479],{"class":661},"**",[655,1481,1482],{"class":706}," 2",[655,1484,1485],{"class":661}," +",[655,1487,1488],{"class":669}," this.y ",[655,1490,1479],{"class":661},[655,1492,1482],{"class":706},[655,1494,1495],{"class":669},") ",[655,1497,1479],{"class":661},[655,1499,1500],{"class":706}," 0.5\n",[655,1502,1503],{"class":657,"line":729},[655,1504,688],{"emptyLinePlaceholder":34},[655,1506,1507,1510,1512,1515,1518,1521,1524],{"class":657,"line":739},[655,1508,1509],{"class":669},"p ",[655,1511,679],{"class":661},[655,1513,1514],{"class":669}," Point(",[655,1516,1517],{"class":706},"3",[655,1519,1520],{"class":669},", ",[655,1522,1523],{"class":706},"4",[655,1525,749],{"class":669},[655,1527,1528,1530],{"class":657,"line":752},[655,1529,755],{"class":706},[655,1531,1532],{"class":669},"(p.distance_from_origin())\n",[22,1534,1536,1553,1563,1578],{"className":1535},[25],[27,1537,1539,1541,1542,1545,1546,1549,1550,1552],{"className":1538},[30],[32,1540],{"disabled":34,"type":35}," Yes — it prints ",[38,1543,1544],{},"5.0","; ",[38,1547,1548],{},"this"," works exactly like ",[38,1551,40],{}," would, since the first parameter name is not special to Python",[27,1554,1556,121,1558,1560,1561],{"className":1555},[30],[32,1557],{"disabled":34,"type":35},[38,1559,254],{},", since instance methods must name their first parameter ",[38,1562,40],{},[27,1564,1566,1568,1569,108,1572,1575,1576],{"className":1565},[30],[32,1567],{"disabled":34,"type":35}," It runs but ",[38,1570,1571],{},"this.x",[38,1573,1574],{},"this.y"," are never actually attached to ",[38,1577,77],{},[27,1579,1581,121,1583],{"className":1580},[30],[32,1582],{"disabled":34,"type":35},[38,1584,1585],{},"TypeError: __init__() takes 3 positional arguments but 4 were given",[70,1587,1588,1590,1601],{},[73,1589,75],{},[77,1591,1592,1594,1595,1545,1597,1549,1599,1552],{},[80,1593,82],{}," A — Yes — it prints ",[38,1596,1544],{},[38,1598,1548],{},[38,1600,40],{},[77,1602,1603,1605,1606,1609,1610,1613,1614,1616],{},[80,1604,92],{}," As established in Q3, Python's method-binding mechanism only cares about parameter ",[126,1607,1608],{},"position",", not name — whichever name occupies the first slot receives the bound instance. The code is fully functional and computes ",[38,1611,1612],{},"sqrt(3² + 4²) = 5.0","; it's simply unconventional and would likely fail code review for violating the near-universal ",[38,1615,40],{}," naming convention, not because of anything the interpreter enforces.",[14,1618,1619,1626,1712,1757],{"language":638},[17,1620,1622,1623,1625],{"id":1621},"q13-calling-a-staticmethod-through-an-instance-versus-through-the-class-whats-the-difference-in-behavior","Q13. Calling a ",[38,1624,478],{}," through an instance versus through the class — what's the difference in behavior?",[644,1627,1628],{"language":638},[647,1629,1631],{"className":649,"code":1630,"language":638,"meta":651,"style":651},"class MathUtils:\n    @staticmethod\n    def add(a, b):\n        return a + b\n\nm = MathUtils()\nprint(MathUtils.add(2, 3), m.add(2, 3))\n",[38,1632,1633,1642,1650,1659,1672,1676,1686],{"__ignoreMap":651},[655,1634,1635,1637,1640],{"class":657,"line":658},[655,1636,662],{"class":661},[655,1638,1639],{"class":665}," MathUtils",[655,1641,670],{"class":669},[655,1643,1644,1647],{"class":657,"line":673},[655,1645,1646],{"class":665},"    @",[655,1648,1649],{"class":706},"staticmethod\n",[655,1651,1652,1654,1656],{"class":657,"line":685},[655,1653,694],{"class":661},[655,1655,697],{"class":665},[655,1657,1658],{"class":669},"(a, b):\n",[655,1660,1661,1663,1666,1669],{"class":657,"line":691},[655,1662,1036],{"class":661},[655,1664,1665],{"class":669}," a ",[655,1667,1668],{"class":661},"+",[655,1670,1671],{"class":669}," b\n",[655,1673,1674],{"class":657,"line":703},[655,1675,688],{"emptyLinePlaceholder":34},[655,1677,1678,1681,1683],{"class":657,"line":713},[655,1679,1680],{"class":669},"m ",[655,1682,679],{"class":661},[655,1684,1685],{"class":669}," MathUtils()\n",[655,1687,1688,1690,1693,1696,1698,1700,1703,1705,1707,1709],{"class":657,"line":718},[655,1689,755],{"class":706},[655,1691,1692],{"class":669},"(MathUtils.add(",[655,1694,1695],{"class":706},"2",[655,1697,1520],{"class":669},[655,1699,1517],{"class":706},[655,1701,1702],{"class":669},"), m.add(",[655,1704,1695],{"class":706},[655,1706,1520],{"class":669},[655,1708,1517],{"class":706},[655,1710,1711],{"class":669},"))\n",[22,1713,1715,1724,1740,1749],{"className":1714},[25],[27,1716,1718,1720,1721,1723],{"className":1717},[30],[32,1719],{"disabled":34,"type":35}," No difference — both print ",[38,1722,350],{},"; a static method never receives an implicit instance argument no matter how it's accessed",[27,1725,1727,121,1729,1732,1733,1735,1736,1739],{"className":1726},[30],[32,1728],{"disabled":34,"type":35},[38,1730,1731],{},"m.add(2, 3)"," raises ",[38,1734,1170],{}," because it implicitly passes ",[38,1737,1738],{},"m"," as an extra argument",[27,1741,1743,121,1745,1748],{"className":1742},[30],[32,1744],{"disabled":34,"type":35},[38,1746,1747],{},"MathUtils.add(2, 3)"," fails because static methods require an instance to be called",[27,1750,1752,1754,1755],{"className":1751},[30],[32,1753],{"disabled":34,"type":35}," The class-level call returns an unbound method object instead of ",[38,1756,350],{},[70,1758,1759,1761,1768],{},[73,1760,75],{},[77,1762,1763,1765,1766,1723],{},[80,1764,82],{}," A — No difference — both print ",[38,1767,350],{},[77,1769,1770,121,1772,1774,1775,1366,1778,1780,1781,1784],{},[80,1771,92],{},[38,1773,478],{}," explicitly opts out of the descriptor-based instance-binding that normal methods get, so accessing it via ",[38,1776,1777],{},"m.add",[126,1779,1369],{}," produce a bound method the way ",[38,1782,1783],{},"m.instance_method"," would — it returns the plain underlying function either way. This is exactly why static methods are safe to call from either the class or an instance with identical results.",[14,1786,1787,1794,1923,1958],{"language":638},[17,1788,1790,1791,1793],{"id":1789},"q14-alternative-constructors-via-classmethod-in-a-subclass-what-prints","Q14. Alternative constructors via ",[38,1792,416],{}," in a subclass — what prints?",[644,1795,1796],{"language":638},[647,1797,1799],{"className":649,"code":1798,"language":638,"meta":651,"style":651},"class Animal:\n    def __init__(self, name):\n        self.name = name\n\n    @classmethod\n    def from_upper(cls, name):\n        return cls(name.upper())\n\nclass Dog(Animal):\n    pass\n\nd = Dog.from_upper(\"rex\")\nprint(type(d).__name__, d.name)\n",[38,1800,1801,1810,1819,1831,1835,1842,1852,1861,1865,1880,1885,1889,1904],{"__ignoreMap":651},[655,1802,1803,1805,1808],{"class":657,"line":658},[655,1804,662],{"class":661},[655,1806,1807],{"class":665}," Animal",[655,1809,670],{"class":669},[655,1811,1812,1814,1816],{"class":657,"line":673},[655,1813,694],{"class":661},[655,1815,872],{"class":706},[655,1817,1818],{"class":669},"(self, name):\n",[655,1820,1821,1823,1826,1828],{"class":657,"line":685},[655,1822,707],{"class":706},[655,1824,1825],{"class":669},".name ",[655,1827,679],{"class":661},[655,1829,1830],{"class":669}," name\n",[655,1832,1833],{"class":657,"line":691},[655,1834,688],{"emptyLinePlaceholder":34},[655,1836,1837,1839],{"class":657,"line":703},[655,1838,1646],{"class":665},[655,1840,1841],{"class":706},"classmethod\n",[655,1843,1844,1846,1849],{"class":657,"line":713},[655,1845,694],{"class":661},[655,1847,1848],{"class":665}," from_upper",[655,1850,1851],{"class":669},"(cls, name):\n",[655,1853,1854,1856,1858],{"class":657,"line":718},[655,1855,1036],{"class":661},[655,1857,1039],{"class":706},[655,1859,1860],{"class":669},"(name.upper())\n",[655,1862,1863],{"class":657,"line":729},[655,1864,688],{"emptyLinePlaceholder":34},[655,1866,1867,1869,1872,1874,1877],{"class":657,"line":739},[655,1868,662],{"class":661},[655,1870,1871],{"class":665}," Dog",[655,1873,1055],{"class":669},[655,1875,1876],{"class":665},"Animal",[655,1878,1879],{"class":669},"):\n",[655,1881,1882],{"class":657,"line":752},[655,1883,1884],{"class":661},"    pass\n",[655,1886,1887],{"class":657,"line":1100},[655,1888,688],{"emptyLinePlaceholder":34},[655,1890,1891,1894,1896,1899,1902],{"class":657,"line":1111},[655,1892,1893],{"class":669},"d ",[655,1895,679],{"class":661},[655,1897,1898],{"class":669}," Dog.from_upper(",[655,1900,1901],{"class":745},"\"rex\"",[655,1903,749],{"class":669},[655,1905,1907,1909,1911,1914,1917,1920],{"class":657,"line":1906},13,[655,1908,755],{"class":706},[655,1910,1055],{"class":669},[655,1912,1913],{"class":706},"type",[655,1915,1916],{"class":669},"(d).",[655,1918,1919],{"class":706},"__name__",[655,1921,1922],{"class":669},", d.name)\n",[22,1924,1926,1934,1942,1950],{"className":1925},[25],[27,1927,1929,121,1931],{"className":1928},[30],[32,1930],{"disabled":34,"type":35},[38,1932,1933],{},"Dog REX",[27,1935,1937,121,1939],{"className":1936},[30],[32,1938],{"disabled":34,"type":35},[38,1940,1941],{},"Animal REX",[27,1943,1945,121,1947],{"className":1944},[30],[32,1946],{"disabled":34,"type":35},[38,1948,1949],{},"Dog rex",[27,1951,1953,121,1955],{"className":1952},[30],[32,1954],{"disabled":34,"type":35},[38,1956,1957],{},"TypeError: from_upper() is not inherited by subclasses",[70,1959,1960,1962,1968],{},[73,1961,75],{},[77,1963,1964,175,1966],{},[80,1965,82],{},[38,1967,1933],{},[77,1969,1970,1972,1973,1975,1976,1979,1980,1982,1983,1986,1987,1990,1991,1993,1994,1996,1997,1999],{},[80,1971,92],{}," Inside a classmethod, ",[38,1974,202],{}," is bound to whichever class the method was actually accessed through — here, ",[38,1977,1978],{},"Dog"," — not the class where the method was defined (",[38,1981,1876],{},"). So ",[38,1984,1985],{},"cls(name.upper())"," calls ",[38,1988,1989],{},"Dog(\"REX\")",", correctly producing a ",[38,1992,1978],{}," instance. This polymorphic behavior is precisely why ",[38,1995,416],{}," (not ",[38,1998,478],{},") is the right tool for alternative constructors meant to be inheritance-friendly.",[14,2001,2002,2006,2054],{},[17,2003,2005],{"id":2004},"q15-for-a-class-that-needs-a-mutable-default-like-a-list-or-dict-as-part-of-an-instances-state-what-is-the-best-practice-approach","Q15. For a class that needs a mutable default (like a list or dict) as part of an instance's state, what is the best-practice approach?",[22,2007,2009,2030,2036,2048],{"className":2008},[25],[27,2010,2012,2014,2015,2017,2018,2021,2022,2025,2026,2029],{"className":2011},[30],[32,2013],{"disabled":34,"type":35}," Initialize it inside ",[38,2016,44],{}," as ",[38,2019,2020],{},"self.attr = []"," (or use ",[38,2023,2024],{},"dataclasses.field(default_factory=list)"," if using ",[38,2027,2028],{},"@dataclass","), never as a bare mutable class attribute",[27,2031,2033,2035],{"className":2032},[30],[32,2034],{"disabled":34,"type":35}," Declare it as a class attribute for efficiency, since it avoids re-allocating a list per instance",[27,2037,2039,2041,2042,2044,2045],{"className":2038},[30],[32,2040],{"disabled":34,"type":35}," Use a mutable default argument in ",[38,2043,44],{},", e.g. ",[38,2046,2047],{},"def __init__(self, items=[]):",[27,2049,2051,2053],{"className":2050},[30],[32,2052],{"disabled":34,"type":35}," Store it as a class attribute and document that callers must not mutate it",[70,2055,2056,2058,2071],{},[73,2057,75],{},[77,2059,2060,2062,2063,2017,2065,2021,2067,2025,2069,2029],{},[80,2061,82],{}," A — Initialize it inside ",[38,2064,44],{},[38,2066,2020],{},[38,2068,2024],{},[38,2070,2028],{},[77,2072,2073,121,2075,2078,2079,2081,2082,2085,2086,2089],{},[80,2074,92],{},[80,2076,2077],{},"Idiom"," — This is the direct fix for the Q8\u002FQ9 gotcha, generalized: any per-instance mutable state belongs in ",[38,2080,44],{}," (or a ",[38,2083,2084],{},"default_factory"," for dataclasses), so each instance gets its own object. A mutable default ",[126,2087,2088],{},"argument"," (option C) is a related but distinct footgun — default argument values are evaluated once at function-definition time and shared across all calls that don't override them, causing the exact same kind of cross-instance leakage.",[14,2091,2092,2103,2144],{},[17,2093,2095,2096,1520,2099,2102],{"id":2094},"q16-when-designing-alternative-ways-to-construct-an-object-eg-configfrom_filepath-configfrom_env-whats-the-idiomatic-choice","Q16. When designing alternative ways to construct an object (e.g., ",[38,2097,2098],{},"Config.from_file(path)",[38,2100,2101],{},"Config.from_env()","), what's the idiomatic choice?",[22,2104,2106,2117,2125,2135],{"className":2105},[25],[27,2107,2109,121,2111,389,2113,2116],{"className":2108},[30],[32,2110],{"disabled":34,"type":35},[38,2112,416],{},[38,2114,2115],{},"cls(...)"," is used internally, keeping the constructors correct for subclasses too",[27,2118,2120,121,2122,2124],{"className":2119},[30],[32,2121],{"disabled":34,"type":35},[38,2123,478],{},", since these are just utility functions that don't need class state",[27,2126,2128,2130,2131,2134],{"className":2127},[30],[32,2129],{"disabled":34,"type":35}," A plain module-level function that returns a hardcoded ",[38,2132,2133],{},"Config(...)"," call",[27,2136,2138,2140,2141,2143],{"className":2137},[30],[32,2139],{"disabled":34,"type":35}," Overloading ",[38,2142,44],{}," with many optional parameters instead of separate constructors",[70,2145,2146,2148,2156],{},[73,2147,75],{},[77,2149,2150,175,2152,389,2154,2116],{},[80,2151,82],{},[38,2153,416],{},[38,2155,2115],{},[77,2157,2158,121,2160,2162,2163,2165,2166,2168,2169,2171,2172,1520,2175,2178],{},[80,2159,92],{},[80,2161,2077],{}," — As shown in Q14, a ",[38,2164,416],{}," that calls ",[38,2167,2115],{}," automatically does the right thing for subclasses, while a ",[38,2170,478],{}," (option B) would have to hardcode the concrete class name, silently breaking for any subclass that calls the \"constructor.\" This is the standard pattern behind ",[38,2173,2174],{},"dict.fromkeys",[38,2176,2177],{},"datetime.fromtimestamp",", and similar APIs in the standard library.",[14,2180,2181,2190,2234],{},[17,2182,2184,2185,2187,2188,112],{"id":2183},"q17-when-is-it-actually-necessary-to-override-__new__-instead-of-just-using-__init__","Q17. When is it actually necessary to override ",[38,2186,111],{}," instead of just using ",[38,2189,44],{},[22,2191,2193,2210,2219,2225],{"className":2192},[25],[27,2194,2196,2198,2199,2202,2203,533,2206,2209],{"className":2195},[30],[32,2197],{"disabled":34,"type":35}," When you need to control instance ",[126,2200,2201],{},"creation"," itself — e.g., returning a cached\u002Fsingleton instance, or subclassing an immutable built-in type like ",[38,2204,2205],{},"str",[38,2207,2208],{},"tuple"," where state must be set during creation",[27,2211,2213,2215,2216,2218],{"className":2212},[30],[32,2214],{"disabled":34,"type":35}," Whenever a class defines more than one ",[38,2217,44],{}," parameter",[27,2220,2222,2224],{"className":2221},[30],[32,2223],{"disabled":34,"type":35}," Every time a class is used with inheritance",[27,2226,2228,2230,2231,2233],{"className":2227},[30],[32,2229],{"disabled":34,"type":35}," Never — ",[38,2232,111],{}," is a legacy hook with no valid modern use case",[70,2235,2236,2238,2249],{},[73,2237,75],{},[77,2239,2240,2242,2243,2202,2245,533,2247,2209],{},[80,2241,82],{}," A — When you need to control instance ",[126,2244,2201],{},[38,2246,2205],{},[38,2248,2208],{},[77,2250,2251,121,2253,811,2255,2257,2258,2261,2262,2264,2265,2267,2268,2270,2271,2273],{},[80,2252,92],{},[80,2254,2077],{},[38,2256,44],{}," can only ",[126,2259,2260],{},"mutate"," an already-created instance, which doesn't work for immutable types (you can't \"mutate\" a ",[38,2263,2205],{}," after creation) or for patterns where you might want to return an existing object instead of a new one (singletons, object pools, memoized value types). For ordinary mutable classes, overriding ",[38,2266,111],{}," is unnecessary complexity — ",[38,2269,44],{}," alone is the idiomatic choice, which is why it's rare to see ",[38,2272,111],{}," overrides in typical application code.",[14,2275,2276,2287,2324],{},[17,2277,2279,2280,2283,2284,2286],{"id":2278},"q18-a-team-debates-whether-max_retries-3-should-be-a-class-attribute-or-set-in-__init__-given-its-an-immutable-shared-constant-that-instances-never-need-to-override-individually-whats-the-best-practice","Q18. A team debates whether ",[38,2281,2282],{},"MAX_RETRIES = 3"," should be a class attribute or set in ",[38,2285,44],{},". Given it's an immutable, shared constant that instances never need to override individually, what's the best practice?",[22,2288,2290,2299,2308,2314],{"className":2289},[25],[27,2291,2293,2295,2296],{"className":2292},[30],[32,2294],{"disabled":34,"type":35}," Keep it as a class attribute — it's immutable, so there's no shared-mutation risk, and instances can still override it individually if ever needed via ",[38,2297,2298],{},"self.MAX_RETRIES = ...",[27,2300,2302,2304,2305,2307],{"className":2301},[30],[32,2303],{"disabled":34,"type":35}," Always move every attribute into ",[38,2306,44],{},", regardless of mutability, to avoid any confusion",[27,2309,2311,2313],{"className":2310},[30],[32,2312],{"disabled":34,"type":35}," Store it in a global module-level variable instead of attaching it to the class at all",[27,2315,2317,2319,2320,2323],{"className":2316},[30],[32,2318],{"disabled":34,"type":35}," Make it a ",[38,2321,2322],{},"@property"," that returns a hardcoded value",[70,2325,2326,2328,2335],{},[73,2327,75],{},[77,2329,2330,2332,2333],{},[80,2331,82],{}," A — Keep it as a class attribute — it's immutable, so there's no shared-mutation risk, and instances can still override it individually if ever needed via ",[38,2334,2298],{},[77,2336,2337,121,2339,2341,2342,2345,2346,2349,2350,2352],{},[80,2338,92],{},[80,2340,2077],{}," — The Q8 gotcha specifically concerns ",[126,2343,2344],{},"mutable"," class attributes (lists, dicts, sets) being mutated in place and shared unexpectedly. An immutable constant like an ",[38,2347,2348],{},"int"," has no such risk: any attempt to \"change\" it on an instance creates a new instance attribute (as in Q11) rather than corrupting shared state, so class-level placement is both idiomatic and efficient (one shared object instead of a per-instance copy). The rule isn't \"never use class attributes\" — it's \"don't use ",[126,2351,2344],{}," class attributes for per-instance state.\"",[14,2354,2355,2361,2390],{},[17,2356,2358,2359,112],{"id":2357},"q19-which-situation-makes-a-plain-module-level-function-more-appropriate-than-a-staticmethod","Q19. Which situation makes a plain module-level function more appropriate than a ",[38,2360,478],{},[22,2362,2364,2370,2378,2384],{"className":2363},[25],[27,2365,2367,2369],{"className":2366},[30],[32,2368],{"disabled":34,"type":35}," When the function has no meaningful connection to the class's purpose or namespace, and grouping it under the class only adds indirection without organizational benefit",[27,2371,2373,2375,2376],{"className":2372},[30],[32,2374],{"disabled":34,"type":35}," Whenever the function doesn't use ",[38,2377,40],{},[27,2379,2381,2383],{"className":2380},[30],[32,2382],{"disabled":34,"type":35}," Whenever the function is longer than a few lines",[27,2385,2387,2389],{"className":2386},[30],[32,2388],{"disabled":34,"type":35}," Static methods are always better than module functions because they're faster to call",[70,2391,2392,2394,2399],{},[73,2393,75],{},[77,2395,2396,2398],{},[80,2397,82],{}," A — When the function has no meaningful connection to the class's purpose or namespace, and grouping it under the class only adds indirection without organizational benefit",[77,2400,2401,121,2403,811,2405,2407,2408,2411,2412,2415,2416,2419,2420,2422],{},[80,2402,92],{},[80,2404,2077],{},[38,2406,478],{}," exists purely for ",[126,2409,2410],{},"namespacing"," — grouping a helper under a class because it's conceptually related (e.g., ",[38,2413,2414],{},"Vector.from_polar","-style helpers, or validation helpers tightly coupled to the class's data). If a function is genuinely general-purpose and unrelated to the class, forcing it into the class as a static method just adds an extra ",[38,2417,2418],{},"ClassName."," prefix everywhere it's used, with no benefit — a plain module function is more discoverable and idiomatic in that case. \"Doesn't use ",[38,2421,40],{},"\" (option B) is necessary but not sufficient justification on its own.",[14,2424,2425,2437,2480],{},[17,2426,2428,2429,2432,2433,2436],{"id":2427},"q20-reviewing-a-pull-request-you-see-typeobj-foo-used-to-check-an-objects-type-instead-of-isinstanceobj-foo-why-is-this-flagged-as-a-best-practice-issue","Q20. Reviewing a pull request, you see ",[38,2430,2431],{},"type(obj) == Foo"," used to check an object's type instead of ",[38,2434,2435],{},"isinstance(obj, Foo)",". Why is this flagged as a best-practice issue?",[22,2438,2440,2454,2462,2468],{"className":2439},[25],[27,2441,2443,121,2445,2448,2449,2451,2452],{"className":2442},[30],[32,2444],{"disabled":34,"type":35},[38,2446,2447],{},"isinstance"," respects inheritance (so subclass instances are correctly recognized) and works with virtual base classes\u002FABCs, while ",[38,2450,2431],{}," fails for any subclass instance even though it's substitutable for ",[38,2453,320],{},[27,2455,2457,121,2459,2461],{"className":2456},[30],[32,2458],{"disabled":34,"type":35},[38,2460,2431],{}," is slower in every case and should never be used for performance reasons",[27,2463,2465,2467],{"className":2464},[30],[32,2466],{"disabled":34,"type":35}," They are functionally identical; the difference is purely stylistic",[27,2469,2471,121,2473,2475,2476,533,2478],{"className":2470},[30],[32,2472],{"disabled":34,"type":35},[38,2474,2447],{}," cannot check against built-in types like ",[38,2477,2348],{},[38,2479,2205],{},[70,2481,2482,2484,2494],{},[73,2483,75],{},[77,2485,2486,175,2488,2448,2490,2451,2492],{},[80,2487,82],{},[38,2489,2447],{},[38,2491,2431],{},[38,2493,320],{},[77,2495,2496,121,2498,2500,2501,1520,2504,1186,2507,2510,2511,1186,2514,2517,2518,2521,2522,2525,2526,2528],{},[80,2497,92],{},[80,2499,2077],{}," — Given ",[38,2502,2503],{},"class Dog(Animal): ...",[38,2505,2506],{},"isinstance(Dog(), Animal)",[38,2508,2509],{},"True"," (correctly honoring polymorphism), but ",[38,2512,2513],{},"type(Dog()) == Animal",[38,2515,2516],{},"False",", since ",[38,2519,2520],{},"type()"," returns the ",[126,2523,2524],{},"exact"," class, not considering the MRO. Code that relies on exact-type checks silently breaks the moment someone introduces a reasonable subclass, which is precisely the kind of duck-typing\u002Fpolymorphism violation ",[38,2527,2447],{}," is designed to avoid. This ties directly into duck typing and explicit type checks covered in the next quiz on inheritance.",[2530,2531,2532],"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 .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":651,"searchDepth":673,"depth":673,"links":2534},[2535,2536,2538,2540,2542,2544,2546,2548,2549,2551,2552,2553,2554,2556,2558,2559,2561,2563,2565,2567],{"id":19,"depth":685,"text":20},{"id":104,"depth":685,"text":2537},"Q2. What is the difference between __init__ and __new__?",{"id":220,"depth":685,"text":2539},"Q3. Is self a reserved keyword in Python?",{"id":297,"depth":685,"text":2541},"Q4. In class Foo: def bar(self, x): ..., what gets passed as self when you call Foo().bar(5)?",{"id":412,"depth":685,"text":2543},"Q5. What distinguishes a @classmethod from a regular instance method?",{"id":474,"depth":685,"text":2545},"Q6. What distinguishes a @staticmethod from both instance and class methods?",{"id":540,"depth":685,"text":2547},"Q7. In what order does Python call __new__ and __init__ when you write Foo(x, y)?",{"id":641,"depth":685,"text":642},{"id":842,"depth":685,"text":2550},"Q9. Which fix correctly gives each ShoppingCart instance its own independent items list?",{"id":990,"depth":685,"text":991},{"id":1244,"depth":685,"text":1245},{"id":1409,"depth":685,"text":1410},{"id":1621,"depth":685,"text":2555},"Q13. Calling a @staticmethod through an instance versus through the class — what's the difference in behavior?",{"id":1789,"depth":685,"text":2557},"Q14. Alternative constructors via @classmethod in a subclass — what prints?",{"id":2004,"depth":685,"text":2005},{"id":2094,"depth":685,"text":2560},"Q16. When designing alternative ways to construct an object (e.g., Config.from_file(path), Config.from_env()), what's the idiomatic choice?",{"id":2183,"depth":685,"text":2562},"Q17. When is it actually necessary to override __new__ instead of just using __init__?",{"id":2278,"depth":685,"text":2564},"Q18. A team debates whether MAX_RETRIES = 3 should be a class attribute or set in __init__. Given it's an immutable, shared constant that instances never need to override individually, what's the best practice?",{"id":2357,"depth":685,"text":2566},"Q19. Which situation makes a plain module-level function more appropriate than a @staticmethod?",{"id":2427,"depth":685,"text":2568},"Q20. Reviewing a pull request, you see type(obj) == Foo used to check an object's type instead of isinstance(obj, Foo). Why is this flagged as a best-practice issue?","md",{},"\u002Fpython\u002F12-classes-and-objects",{"title":5,"description":651},"python\u002F12-classes-and-objects","ye736CW0dJKaw4b0y8zt8uTyM-vawgrxo5GM1AG_HYI",1787335398261]