[{"data":1,"prerenderedAt":263},["ShallowReactive",2],{"topic-python-oop":3},{"framework":4,"topic":15,"subtopics":24},{"id":5,"description":6,"extension":7,"icon":8,"meta":9,"name":10,"order":11,"slug":8,"stem":12,"tier":13,"__hash__":14},"frameworks\u002Fframeworks\u002Fpython.yml","Python interview questions across language fundamentals, data structures and common gotchas for backend, data and automation roles.","yml","python",{},"Python",3,"frameworks\u002Fpython",1,"QsijsotyAr-3rnhJDWWZmc7hE6HAhylS5t1dKpigMOA",{"id":16,"description":17,"extension":7,"frameworkSlug":8,"meta":18,"name":19,"order":20,"slug":21,"stem":22,"__hash__":23},"topics\u002Ftopics\u002Fpython-oop.yml","Classes, inheritance and the MRO, dunder methods, properties and dataclasses — object-oriented Python and its data model.",{},"Object-Oriented Programming",5,"oop","topics\u002Fpython-oop","HWQNuZjsu-ieQLL6BEbbeZIHJCEhXocNXPj2wzkQUbQ",[25,70,109,152,188,227],{"id":26,"title":27,"body":28,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":37,"navigation":38,"order":13,"path":39,"questions":40,"related":63,"seo":64,"seoDescription":65,"stem":66,"subtopic":67,"topic":19,"topicSlug":21,"updated":68,"__hash__":69},"qa\u002Fpython\u002Foop\u002Finheritance.md","Inheritance",{"type":29,"value":30,"toc":31},"minimark",[],{"title":32,"searchDepth":33,"depth":33,"links":34},"",2,[],"hard","md",{},true,"\u002Fpython\u002Foop\u002Finheritance",[41,46,50,54,58],{"id":42,"difficulty":43,"q":44,"a":45},"single-vs-multiple","easy","What is the difference between single and multiple inheritance?","**Single inheritance** means a class derives from exactly one parent; **multiple\ninheritance** means it lists more than one base class. Python supports both —\nmultiple inheritance is what lets you compose behaviour from several sources at\nonce.\n\n```python\nclass Animal:\n    def eat(self): print(\"eating\")\n\nclass Dog(Animal):          # single inheritance\n    def bark(self): print(\"woof\")\n\nclass Swimmer: ...\nclass Flyer: ...\nclass Duck(Animal, Swimmer, Flyer):  # multiple inheritance\n    pass\n```\n\nMultiple inheritance is powerful but can create ambiguity about *which* parent's\nmethod wins — that ambiguity is resolved by the **MRO**. Rule of thumb: prefer\nsingle inheritance plus small **mixins** over deep, wide hierarchies.\n",{"id":47,"difficulty":35,"q":48,"a":49},"what-is-mro","What is the MRO and how is it computed?","The **MRO (Method Resolution Order)** is the linear, ordered list of classes\nPython searches when looking up an attribute or method on an instance. CPython\nbuilds it with the **C3 linearization** algorithm, which guarantees a consistent\norder that respects each class's own order of bases and never places a parent\nbefore its child.\n\n```python\nclass A: ...\nclass B(A): ...\nclass C(A): ...\nclass D(B, C): ...\n\nD.__mro__            # (D, B, C, A, object)\nD.mro()             # same, as a list\n```\n\nC3 produces a single deterministic order (or raises `TypeError` if no consistent\norder exists). Attribute lookup walks this list left to right and stops at the\nfirst match. Rule of thumb: read `Cls.__mro__` whenever multiple inheritance\nsurprises you — it tells you exactly who wins.\n",{"id":51,"difficulty":35,"q":52,"a":53},"how-super-works","How does super() actually work?","`super()` does **not** simply call \"the parent class\" — it calls the **next class\nin the instance's MRO**, starting after the current class. That cooperative\nbehaviour is what makes multiple inheritance work correctly: each class delegates\nto whatever comes next, regardless of the static hierarchy.\n\n```python\nclass A:\n    def greet(self): print(\"A\")\nclass B(A):\n    def greet(self): print(\"B\"); super().greet()\nclass C(A):\n    def greet(self): print(\"C\"); super().greet()\nclass D(B, C):\n    def greet(self): print(\"D\"); super().greet()\n\nD().greet()   # D, B, C, A  — follows D.__mro__, not B's parent\n```\n\nNote `super().greet()` inside `B` calls `C`, not `A`, because the MRO of a `D`\ninstance puts `C` after `B`. Rule of thumb: in a cooperative hierarchy every\noverride should call `super()` so the whole chain runs exactly once.\n",{"id":55,"difficulty":35,"q":56,"a":57},"diamond-problem","What is the diamond problem and how does Python solve it?","The **diamond problem** arises when two classes (`B`, `C`) inherit from a common\nbase (`A`), and a fourth class (`D`) inherits from both. The question is: when\n`D` calls an inherited method, is `A`'s code run **once or twice**? Naive\nlanguages run it twice; Python's **C3 MRO** guarantees `A` appears **exactly\nonce**, so cooperative `super()` calls run it a single time.\n\n```python\nclass A:\n    def __init__(self): print(\"A\"); \nclass B(A):\n    def __init__(self): print(\"B\"); super().__init__()\nclass C(A):\n    def __init__(self): print(\"C\"); super().__init__()\nclass D(B, C):\n    def __init__(self): print(\"D\"); super().__init__()\n\nD()   # D, B, C, A  — A's __init__ runs ONCE\n```\n\nThe MRO `(D, B, C, A, object)` linearizes the diamond into a clean chain. Rule of\nthumb: the diamond is only safe when every class in it uses `super()` consistently\n— mixing `super()` with hard-coded `Base.__init__(self)` calls breaks the\nguarantee.\n",{"id":59,"difficulty":60,"q":61,"a":62},"mixins-and-abc","medium","What are mixins and abstract base classes?","A **mixin** is a small class that provides a focused slice of behaviour meant to be\ncombined into other classes via multiple inheritance — it isn't useful on its own\nand usually has no `__init__`. An **abstract base class (ABC)**, from the `abc`\nmodule, defines an interface with `@abstractmethod`s and **cannot be instantiated**\nuntil every abstract method is overridden.\n\n```python\nfrom abc import ABC, abstractmethod\n\nclass JsonMixin:                 # mixin: adds one capability\n    def to_json(self): import json; return json.dumps(self.__dict__)\n\nclass Shape(ABC):                # abstract base: defines a contract\n    @abstractmethod\n    def area(self): ...\n\nclass Circle(Shape, JsonMixin):\n    def __init__(self, r): self.r = r\n    def area(self): return 3.14159 * self.r ** 2\n\nShape()    # TypeError: can't instantiate abstract class\n```\n\nUse mixins to share reusable behaviour and ABCs to enforce that subclasses\nimplement a required interface (`isinstance` checks also work against ABCs). Rule\nof thumb: mixins say \"you *can* do this\", ABCs say \"you *must* do this\".\n",null,{"description":32},"Python interview questions on single vs multiple inheritance, the MRO and C3 linearization, super(), the diamond problem, mixins, and abstract base classes.","python\u002Foop\u002Finheritance","Inheritance & the MRO","2026-06-18","lr7N1CknUWOM5mCsCVypmlwzPFk9qinVKOEqgWAED8Y",{"id":71,"title":72,"body":73,"description":32,"difficulty":43,"extension":36,"framework":10,"frameworkSlug":8,"meta":77,"navigation":38,"order":33,"path":78,"questions":79,"related":63,"seo":104,"seoDescription":105,"stem":106,"subtopic":107,"topic":19,"topicSlug":21,"updated":68,"__hash__":108},"qa\u002Fpython\u002Foop\u002Fclasses.md","Classes",{"type":29,"value":74,"toc":75},[],{"title":32,"searchDepth":33,"depth":33,"links":76},[],{},"\u002Fpython\u002Foop\u002Fclasses",[80,84,88,92,96,100],{"id":81,"difficulty":43,"q":82,"a":83},"class-vs-instance","What is the difference between a class and an instance?","A **class** is a **blueprint** — it defines the attributes and methods that\nobjects of that type will have. An **instance** is a concrete **object built\nfrom that blueprint**, with its own state. You write the class once and create\nmany instances from it.\n\n```python\nclass Dog:                 # the blueprint\n    def __init__(self, name):\n        self.name = name   # per-instance state\n\nrex = Dog(\"Rex\")           # an instance\nfido = Dog(\"Fido\")         # a separate instance\nrex.name                   # 'Rex'\nfido.name                  # 'Fido' — independent state\ntype(rex)                  # \u003Cclass '__main__.Dog'>\n```\n\nThe class itself is also an object (of type `type`). Each instance carries its\nown data but shares the class's methods. Think of the class as the cookie\ncutter and the instances as the cookies.\n",{"id":85,"difficulty":60,"q":86,"a":87},"init-vs-new","What is the difference between __init__ and __new__?","`__new__` **creates and returns the new object**; `__init__` **initializes**\nthat already-created object. `__new__` runs first and is a static method that\nreceives the **class**; `__init__` runs second and receives the **instance**\n(`self`) it should configure. `__init__` must return `None`.\n\n```python\nclass Widget:\n    def __new__(cls, *args):\n        print(\"__new__ — allocating\")\n        return super().__new__(cls)   # returns the instance\n    def __init__(self, size):\n        print(\"__init__ — configuring\")\n        self.size = size              # sets state on self\n\nw = Widget(10)   # prints __new__ then __init__\n```\n\nYou rarely override `__new__` — it's mainly for **immutable types** (subclassing\n`int`\u002F`str`\u002F`tuple`), singletons, or metaclass tricks. For everyday classes,\njust use `__init__`.\n",{"id":89,"difficulty":43,"q":90,"a":91},"what-is-self","What is `self` in Python?","`self` is the **instance the method was called on** — it's how a method accesses\nthat object's attributes and other methods. It isn't a keyword; it's just the\nconventional **name of the first parameter**. Python passes the instance\nautomatically when you call `obj.method()`.\n\n```python\nclass Counter:\n    def __init__(self):\n        self.count = 0\n    def increment(self):\n        self.count += 1        # self refers to this instance\n\nc = Counter()\nc.increment()                  # Python passes c as self\nCounter.increment(c)           # exactly equivalent — self is explicit here\n```\n\nSo `c.increment()` is sugar for `Counter.increment(c)`. The explicitness is\ndeliberate — Python makes the instance visible rather than hiding it like\n`this` in other languages.\n",{"id":93,"difficulty":60,"q":94,"a":95},"instance-vs-class-attributes","What is the difference between instance and class attributes?","A **class attribute** is defined in the class body and **shared by every\ninstance**; an **instance attribute** is set on `self` (usually in `__init__`)\nand is **unique per object**. Attribute lookup checks the instance first, then\nfalls back to the class.\n\n```python\nclass Dog:\n    species = \"Canis familiaris\"   # class attribute — shared\n    def __init__(self, name):\n        self.name = name           # instance attribute — per-object\n\na, b = Dog(\"Rex\"), Dog(\"Fido\")\na.species                # 'Canis familiaris' (from the class)\na.name, b.name           # 'Rex', 'Fido' (independent)\na.species = \"wolf\"       # creates an instance attr that SHADOWS the class one\nb.species                # still 'Canis familiaris'\n```\n\nWatch the classic trap: a **mutable** class attribute (like `[]`) is shared and\nwill leak state between instances — initialize mutable state in `__init__`.\n",{"id":97,"difficulty":60,"q":98,"a":99},"repr-vs-str","What is the difference between __repr__ and __str__?","`__repr__` is the **unambiguous, developer-facing** representation — ideally\nsomething that could recreate the object — and is what you see in the REPL and\nin containers. `__str__` is the **readable, user-facing** string used by\n`print()` and `str()`. If `__str__` is missing, Python **falls back to\n`__repr__`**.\n\n```python\nclass Point:\n    def __init__(self, x, y):\n        self.x, self.y = x, y\n    def __repr__(self):\n        return f\"Point(x={self.x}, y={self.y})\"   # for developers\n    def __str__(self):\n        return f\"({self.x}, {self.y})\"            # for users\n\np = Point(1, 2)\nprint(p)     # (1, 2)        — __str__\nrepr(p)      # 'Point(x=1, y=2)'  — __repr__\n[p]          # [Point(x=1, y=2)]  — containers use __repr__\n```\n\nRule of thumb: always define `__repr__`; add `__str__` only when you need a\ndistinct friendly form.\n",{"id":101,"difficulty":60,"q":102,"a":103},"object-creation-flow","What happens, step by step, when you call ClassName()?","Calling `ClassName(args)` invokes the class's metaclass `__call__`, which\norchestrates two steps: it calls **`__new__(cls, args)`** to allocate the\nobject, then — if `__new__` returned an instance of `cls` — calls\n**`__init__(instance, args)`** to initialize it, and finally returns the\ninstance.\n\n```python\nclass Demo:\n    def __new__(cls, *a):\n        print(\"1. __new__\")\n        return super().__new__(cls)\n    def __init__(self, *a):\n        print(\"2. __init__\")\n\nd = Demo()       # prints: 1. __new__  then  2. __init__\n# 3. d is now bound to the fully initialized instance\n```\n\nKey subtlety: if `__new__` returns an object that is **not** an instance of the\nclass, `__init__` is **skipped entirely**. For normal classes you never see\nthis machinery — you just call the class and get back a ready object.\n",{"description":32},"Python interview questions on classes vs instances, __init__ vs __new__, the self parameter, instance vs class attributes, __repr__ vs __str__, and the object creation flow.","python\u002Foop\u002Fclasses","Classes, Instances & __init__","ouDK0ESDWwDR7Ofpyzn1mO8dgBG_yOsNKMgJuy_V_Uo",{"id":110,"title":111,"body":112,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":116,"navigation":38,"order":11,"path":117,"questions":118,"related":63,"seo":147,"seoDescription":148,"stem":149,"subtopic":150,"topic":19,"topicSlug":21,"updated":68,"__hash__":151},"qa\u002Fpython\u002Foop\u002Fdunder-methods.md","Dunder Methods",{"type":29,"value":113,"toc":114},[],{"title":32,"searchDepth":33,"depth":33,"links":115},[],{},"\u002Fpython\u002Foop\u002Fdunder-methods",[119,123,127,131,135,139,143],{"id":120,"difficulty":60,"q":121,"a":122},"what-are-dunder-methods","What are dunder (magic) methods?","**Dunder methods** (\"double underscore\", e.g. `__init__`, `__len__`) are special\nhooks Python calls **implicitly** to make your objects work with built-in syntax\nand functions. They are how Python implements **operator overloading and\nprotocols** — `len(x)` calls `x.__len__()`, `x + y` calls `x.__add__(y)`,\n`x[0]` calls `x.__getitem__(0)`.\n\n```python\nclass Box:\n    def __init__(self, items):\n        self.items = items\n    def __len__(self):\n        return len(self.items)   # makes len(box) work\n\nb = Box([1, 2, 3])\nlen(b)        # 3 — Python calls b.__len__()\n```\n\nThey're sometimes called \"magic methods\" but there's no magic — they're a\ndocumented protocol. Implementing the right dunders makes your objects feel like\nnative Python types.\n",{"id":124,"difficulty":60,"q":125,"a":126},"repr-str-dunder","How do __repr__ and __str__ control how an object prints?","`__repr__` defines the **unambiguous developer representation** (REPL output,\ncontainers, `repr()`); `__str__` defines the **human-readable** form (`print()`,\n`str()`). When `__str__` is absent, Python **falls back to `__repr__`** — so\n`__repr__` is the one to always implement.\n\n```python\nclass Temperature:\n    def __init__(self, c):\n        self.c = c\n    def __repr__(self):\n        return f\"Temperature({self.c})\"     # eval-friendly\n    def __str__(self):\n        return f\"{self.c}°C\"                 # friendly\n\nt = Temperature(20)\nstr(t)    # '20°C'\nrepr(t)   # 'Temperature(20)'\n```\n\nA good `__repr__` ideally lets `eval(repr(obj))` reconstruct the object. Always\ndefine `__repr__`; add `__str__` only when a separate user-facing string helps.\n",{"id":128,"difficulty":35,"q":129,"a":130},"eq-and-hash","How do __eq__ and __hash__ work together?","`__eq__` defines **value equality** (`==`); `__hash__` returns the integer used\nby **dicts and sets**. They have a **contract**: if `a == b` then\n`hash(a) == hash(b)`. Defining `__eq__` **alone** sets `__hash__ = None`, making\ninstances **unhashable** — so define both, derived from the **same immutable\nfields**.\n\n```python\nclass Point:\n    def __init__(self, x, y):\n        self.x, self.y = x, y\n    def __eq__(self, other):\n        return (self.x, self.y) == (other.x, other.y)\n    def __hash__(self):\n        return hash((self.x, self.y))    # same fields as __eq__\n\n{Point(1, 2), Point(1, 2)}   # one element — treated as equal\n```\n\nOnly hash on fields that never change after creation. If you break the contract,\nobjects you store in a set\u002Fdict become unfindable.\n",{"id":132,"difficulty":35,"q":133,"a":134},"operator-overloading","How do you overload operators like + and *?","Implement the matching **arithmetic dunder**: `__add__` for `+`, `__sub__` for\n`-`, `__mul__` for `*`, `__lt__` for `\u003C`, and so on. Python calls them when the\noperator is used. Return a **new object** (or `NotImplemented` if you can't\nhandle the other operand, so Python can try the reflected method like\n`__radd__`).\n\n```python\nclass Vector:\n    def __init__(self, x, y):\n        self.x, self.y = x, y\n    def __add__(self, other):\n        return Vector(self.x + other.x, self.y + other.y)   # v1 + v2\n    def __mul__(self, k):\n        return Vector(self.x * k, self.y * k)               # v * 3\n    def __repr__(self):\n        return f\"Vector({self.x}, {self.y})\"\n\nVector(1, 2) + Vector(3, 4)   # Vector(4, 6)\nVector(1, 2) * 3              # Vector(3, 6)\n```\n\nReturn `NotImplemented` (not raise) for unsupported types so Python can fall\nback gracefully. Don't overload operators in surprising ways — keep semantics\nintuitive.\n",{"id":136,"difficulty":35,"q":137,"a":138},"sequence-protocol","How do __len__ and __getitem__ make an object behave like a sequence?","Implementing `__len__` makes `len(obj)` work, and `__getitem__` makes\n**indexing, slicing, and iteration** work — Python can iterate by calling\n`__getitem__(0)`, `__getitem__(1)`, ... until `IndexError`, even without an\n`__iter__`. Together they form the **sequence protocol**.\n\n```python\nclass Playlist:\n    def __init__(self, songs):\n        self.songs = songs\n    def __len__(self):\n        return len(self.songs)         # len(pl)\n    def __getitem__(self, i):\n        return self.songs[i]           # pl[0], pl[1:3], and iteration\n\npl = Playlist([\"a\", \"b\", \"c\"])\nlen(pl)          # 3\npl[1]            # 'b'\nfor s in pl:     # iterates via __getitem__\n    print(s)\n```\n\nAdd `__contains__` for `in` and `__setitem__` for assignment. This duck-typed\nprotocol is why custom containers feel like lists.\n",{"id":140,"difficulty":60,"q":141,"a":142},"call-dunder","What does __call__ do?","`__call__` makes an **instance itself callable** like a function — `obj()`\ninvokes `obj.__call__()`. This lets objects **carry state between calls**, which\na plain function can't do as cleanly. It's the basis of function objects and\nmany decorators.\n\n```python\nclass Multiplier:\n    def __init__(self, factor):\n        self.factor = factor       # remembered state\n    def __call__(self, x):\n        return x * self.factor     # obj(x)\n\ndouble = Multiplier(2)\ndouble(5)         # 10 — calling the instance\ncallable(double)  # True\n```\n\nUse it for **stateful callables** — configurable functions, accumulators, or\nclass-based decorators. If you just need behavior without state, a closure or\nplain function is simpler.\n",{"id":144,"difficulty":60,"q":145,"a":146},"total-ordering","What does functools.total_ordering do?","`@functools.total_ordering` is a class decorator that **fills in the missing\ncomparison operators** from the ones you define. You provide `__eq__` plus **one**\nof `__lt__`, `__le__`, `__gt__`, `__ge__`, and it generates the rest — saving you\nfrom writing all six.\n\n```python\nfrom functools import total_ordering\n\n@total_ordering\nclass Version:\n    def __init__(self, n):\n        self.n = n\n    def __eq__(self, other):\n        return self.n == other.n\n    def __lt__(self, other):\n        return self.n \u003C other.n     # only this + __eq__ needed\n\nVersion(1) \u003C Version(2)    # True\nVersion(2) >= Version(1)   # True — generated by total_ordering\nsorted([Version(3), Version(1), Version(2)])  # works\n```\n\nThe trade-off is a small performance cost from derived comparisons; for\nhot-path code, define all operators explicitly. Otherwise it's a clean way to\nget full ordering with minimal boilerplate.\n",{"description":32},"Python interview questions on dunder\u002Fmagic methods — __repr__\u002F__str__, __eq__ and __hash__, operator overloading with __add__, the sequence protocol (__len__\u002F__getitem__), __call__, and functools.total_ordering.","python\u002Foop\u002Fdunder-methods","Dunder \u002F Magic Methods","ncqznv9sUn2_SLebvm5cJygSM3_p-P2icZsoSaPIlxo",{"id":153,"title":154,"body":155,"description":32,"difficulty":60,"extension":36,"framework":10,"frameworkSlug":8,"meta":159,"navigation":38,"order":160,"path":161,"questions":162,"related":63,"seo":183,"seoDescription":184,"stem":185,"subtopic":186,"topic":19,"topicSlug":21,"updated":68,"__hash__":187},"qa\u002Fpython\u002Foop\u002Fmethods-properties.md","Methods Properties",{"type":29,"value":156,"toc":157},[],{"title":32,"searchDepth":33,"depth":33,"links":158},[],{},4,"\u002Fpython\u002Foop\u002Fmethods-properties",[163,167,171,175,179],{"id":164,"difficulty":60,"q":165,"a":166},"instance-class-static-methods","What is the difference between an instance method, @classmethod, and @staticmethod?","An **instance method** takes `self` and operates on a specific object. A\n**`@classmethod`** takes `cls` (the class, not an instance) and works on\n**class-level state** or builds instances. A **`@staticmethod`** takes\n**neither** — it's a plain function namespaced inside the class, with no access\nto instance or class state.\n\n```python\nclass Pizza:\n    base_price = 10\n    def __init__(self, toppings):\n        self.toppings = toppings\n    def total(self):                    # instance method — uses self\n        return self.base_price + len(self.toppings)\n    @classmethod\n    def margherita(cls):                # classmethod — uses cls\n        return cls([\"mozzarella\", \"basil\"])\n    @staticmethod\n    def is_valid_topping(name):         # staticmethod — no self\u002Fcls\n        return name.isalpha()\n\nPizza.is_valid_topping(\"ham\")   # True — no instance needed\nPizza.margherita().total()      # 12\n```\n\nUse an instance method for per-object behavior, a classmethod for\nclass-aware logic (e.g. alternative constructors), and a staticmethod for a\nrelated helper that happens to live on the class.\n",{"id":168,"difficulty":60,"q":169,"a":170},"classmethod-constructor","How do you use a classmethod as an alternative constructor?","A classmethod receives `cls`, so it can **build and return a new instance** from\na different input format — giving you multiple named constructors beyond\n`__init__`. Using `cls` (not the hard-coded class name) means **subclasses get\nthe right type** automatically.\n\n```python\nclass Date:\n    def __init__(self, year, month, day):\n        self.year, self.month, self.day = year, month, day\n    @classmethod\n    def from_string(cls, text):\n        y, m, d = map(int, text.split(\"-\"))\n        return cls(y, m, d)             # returns the (sub)class instance\n    @classmethod\n    def today(cls):\n        import datetime\n        t = datetime.date.today()\n        return cls(t.year, t.month, t.day)\n\nDate.from_string(\"2026-06-18\")   # alternative constructor\nDate.today()\n```\n\nThis is the idiomatic Python pattern (`dict.fromkeys`, `datetime.fromtimestamp`)\nfor \"make one of these, but from X\" — clearer than overloading `__init__` with\nflags.\n",{"id":172,"difficulty":60,"q":173,"a":174},"property-getter-setter","How does @property work with a getter and setter?","`@property` turns a method into a **managed attribute** — you access it like\n`obj.x` (no parentheses), but a method runs behind the scenes. The matching\n`@x.setter` runs on assignment, letting you add **validation** without changing\nthe public interface.\n\n```python\nclass Account:\n    def __init__(self, balance):\n        self._balance = balance         # \"private\" backing field\n    @property\n    def balance(self):                  # getter — runs on read\n        return self._balance\n    @balance.setter\n    def balance(self, value):           # setter — runs on write\n        if value \u003C 0:\n            raise ValueError(\"balance cannot be negative\")\n        self._balance = value\n\nacc = Account(100)\nacc.balance          # 100 — looks like an attribute, calls the getter\nacc.balance = 50     # calls the setter (validated)\nacc.balance = -1     # ValueError\n```\n\nThe convention is a `_name` backing attribute behind a public property. Omit the\nsetter to make the property read-only.\n",{"id":176,"difficulty":60,"q":177,"a":178},"why-properties-over-getters","Why do properties beat Java-style getter\u002Fsetter methods?","In Python you **start with a plain attribute** and only convert it to a\n`@property` later **if** you need validation or computation — **without changing\nthe call sites**. So you avoid the Java habit of pre-emptively writing\n`getX()`\u002F`setX()` \"just in case\". The public API stays `obj.x`.\n\n```python\n# Start simple — public attribute:\nclass Circle:\n    def __init__(self, radius):\n        self.radius = radius\n\nc = Circle(5)\nc.radius            # direct access — no ceremony\n\n# Later, add validation transparently — callers don't change:\nclass Circle:\n    def __init__(self, radius):\n        self.radius = radius\n    @property\n    def radius(self):\n        return self._radius\n    @radius.setter\n    def radius(self, value):\n        if value \u003C= 0:\n            raise ValueError(\"radius must be positive\")\n        self._radius = value\n```\n\nThis is the \"uniform access principle\": callers can't tell whether `obj.x` is a\nstored value or computed. Don't write getters\u002Fsetters upfront — reach for a\nproperty only when behavior is needed.\n",{"id":180,"difficulty":43,"q":181,"a":182},"computed-readonly-properties","How do you create a computed or read-only property?","A property with **only a getter** (no setter) is **read-only** — assigning to it\nraises `AttributeError`. A **computed property** derives its value from other\nattributes on each access, so it stays in sync automatically rather than being\nstored.\n\n```python\nclass Rectangle:\n    def __init__(self, width, height):\n        self.width = width\n        self.height = height\n    @property\n    def area(self):                 # computed — derived on each read\n        return self.width * self.height\n\nr = Rectangle(3, 4)\nr.area           # 12 — computed\nr.width = 5\nr.area           # 20 — automatically reflects the change\nr.area = 99      # AttributeError — read-only (no setter)\n```\n\nFor an expensive computation you only want to run once, use\n`functools.cached_property` instead, which caches the result on first access.\nUse a plain read-only property for cheap derived values.\n",{"description":32},"Python interview questions on instance vs classmethod vs staticmethod, classmethods as alternative constructors, the @property getter\u002Fsetter, why properties beat Java-style accessors, and computed\u002Fread-only properties.","python\u002Foop\u002Fmethods-properties","Methods & Properties","EZTMeTHDuvwapkor3EW7aAeDQlUzCDXBSwLIpBJ6zww",{"id":189,"title":190,"body":191,"description":32,"difficulty":60,"extension":36,"framework":10,"frameworkSlug":8,"meta":195,"navigation":38,"order":20,"path":196,"questions":197,"related":63,"seo":222,"seoDescription":223,"stem":224,"subtopic":225,"topic":19,"topicSlug":21,"updated":68,"__hash__":226},"qa\u002Fpython\u002Foop\u002Fdataclasses-slots.md","Dataclasses Slots",{"type":29,"value":192,"toc":193},[],{"title":32,"searchDepth":33,"depth":33,"links":194},[],{},"\u002Fpython\u002Foop\u002Fdataclasses-slots",[198,202,206,210,214,218],{"id":199,"difficulty":43,"q":200,"a":201},"what-dataclass-generates","What does the @dataclass decorator generate for you?","`@dataclass` auto-generates the **boilerplate dunder methods** from the\nclass's annotated fields — primarily **`__init__`**, **`__repr__`**, and\n**`__eq__`**. You declare fields with type hints (and optional defaults) and\nskip writing the constructor by hand.\n\n```python\nfrom dataclasses import dataclass\n\n@dataclass\nclass Point:\n    x: int\n    y: int = 0           # field with a default\n\np = Point(1, 2)\np                        # Point(x=1, y=2)        — generated __repr__\np == Point(1, 2)         # True                   — generated __eq__\n# __init__(self, x, y=0) was generated automatically\n```\n\nYou can opt into more (`order=True` for comparison operators, `frozen=True` for\nimmutability). It removes the repetitive plumbing while leaving normal methods\nup to you.\n",{"id":203,"difficulty":60,"q":204,"a":205},"frozen-dataclass","What does frozen=True do on a dataclass?","`@dataclass(frozen=True)` makes instances **immutable** — assigning to a field\nafter creation raises **`FrozenInstanceError`**. As a bonus, frozen dataclasses\nget a generated **`__hash__`**, so they're usable as **dict keys and set\nmembers**.\n\n```python\nfrom dataclasses import dataclass\n\n@dataclass(frozen=True)\nclass Point:\n    x: int\n    y: int\n\np = Point(1, 2)\np.x = 9            # FrozenInstanceError — immutable\n{p: \"origin\"}      # hashable — works as a dict key\n{Point(1, 2), Point(1, 2)}   # one element — equal and hashable\n```\n\nFrozen dataclasses are the concise, modern way to define **immutable value\nobjects**. Use them whenever an object represents a value that shouldn't change\nafter creation.\n",{"id":207,"difficulty":35,"q":208,"a":209},"default-factory","Why must mutable dataclass defaults use field(default_factory=...)?","A bare mutable default (`tags: list = []`) would be **shared across all\ninstances** — the same default-argument trap as in regular functions — so\ndataclasses **forbid it and raise `ValueError`**. Instead, pass\n**`field(default_factory=list)`**, a zero-arg callable that creates a **fresh\nobject per instance**.\n\n```python\nfrom dataclasses import dataclass, field\n\n@dataclass\nclass Article:\n    title: str\n    tags: list = field(default_factory=list)   # new list each instance\n    # tags: list = []   # would raise ValueError at class-definition time\n\na, b = Article(\"A\"), Article(\"B\")\na.tags.append(\"python\")\nb.tags             # []  — independent, no shared state\n```\n\nUse `default_factory` for any mutable default (`list`, `dict`, `set`) or for a\nvalue that must be computed at construction time. Plain immutable defaults\n(numbers, strings, tuples) are fine inline.\n",{"id":211,"difficulty":35,"q":212,"a":213},"slots","What is __slots__ and what does it buy you?","`__slots__` declares a **fixed set of allowed attributes**, so instances store\nthem in a compact array instead of a per-instance **`__dict__`**. This **saves\nsignificant memory** (no dict per object) and gives **faster attribute access**.\nThe trade-off: you **can't add new attributes** not listed in slots.\n\n```python\nclass Point:\n    __slots__ = (\"x\", \"y\")     # no per-instance __dict__\n    def __init__(self, x, y):\n        self.x, self.y = x, y\n\np = Point(1, 2)\np.x               # 2 — fast access\np.z = 3           # AttributeError — 'z' not in __slots__\n# p.__dict__      # also AttributeError — there is no dict\n\nfrom dataclasses import dataclass\n@dataclass(slots=True)        # dataclasses can generate slots (3.10+)\nclass Fast:\n    x: int\n    y: int\n```\n\nReach for `__slots__` when you create **huge numbers of small objects** and\nmemory matters. For ordinary classes the flexibility of `__dict__` is usually\nworth more than the savings.\n",{"id":215,"difficulty":60,"q":216,"a":217},"dataclass-vs-namedtuple","When would you choose a dataclass over a namedtuple or NamedTuple?","A **`namedtuple`**\u002F**`typing.NamedTuple`** is an **immutable tuple** subclass —\nlightweight, hashable, iterable, and index-accessible, but values can't change.\nA **`@dataclass`** is a regular class — **mutable by default**, supports methods,\ninheritance, and `default_factory`, but isn't a tuple (no unpacking\u002Findexing\nunless you add it).\n\n```python\nfrom typing import NamedTuple\nfrom dataclasses import dataclass\n\nclass PointNT(NamedTuple):     # immutable, tuple-like\n    x: int\n    y: int\nx, y = PointNT(1, 2)           # unpacks like a tuple\n\n@dataclass\nclass PointDC:                 # mutable, full class\n    x: int\n    y: int\n    def shift(self, dx):       # can hold real methods\n        self.x += dx\n\nPointNT(1, 2)[0]   # 1 — index access (tuple)\nPointDC(1, 2).shift(5)         # mutate in place\n```\n\nChoose a **NamedTuple** for small immutable records and tuple semantics; choose a\n**dataclass** when you need mutability, methods, or richer field control. Use\n`frozen=True` dataclasses for immutable value objects that still want class\nfeatures.\n",{"id":219,"difficulty":60,"q":220,"a":221},"post-init","What is __post_init__ used for?","`__post_init__` runs **automatically right after the generated `__init__`** —\nit's the hook for **validation and derived fields** that depend on the\nconstructor arguments. It pairs with `field(init=False)` to declare attributes\nthat aren't constructor parameters but are computed afterward.\n\n```python\nfrom dataclasses import dataclass, field\n\n@dataclass\nclass Rectangle:\n    width: float\n    height: float\n    area: float = field(init=False)     # not a constructor arg\n\n# filled in after __init__:\n    def __post_init__(self):\n        if self.width \u003C= 0 or self.height \u003C= 0:\n            raise ValueError(\"dimensions must be positive\")   # validation\n        self.area = self.width * self.height                  # derived field\n\nr = Rectangle(3, 4)\nr.area            # 12 — computed in __post_init__\nRectangle(-1, 4)  # ValueError\n```\n\nUse `__post_init__` whenever a dataclass needs logic the auto-generated\n`__init__` can't express — validation, normalization, or fields derived from\nothers.\n",{"description":32},"Python interview questions on @dataclass — generated methods, frozen=True, field(default_factory=...) for mutable defaults, __slots__ for memory and speed, dataclass vs namedtuple vs NamedTuple, and __post_init__.","python\u002Foop\u002Fdataclasses-slots","Dataclasses & __slots__","rbgsaK-ivBYw25DXEMFBiwSKrF17EhRciQ-ctqZKl34",{"id":228,"title":229,"body":230,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":234,"navigation":38,"order":235,"path":236,"questions":237,"related":63,"seo":258,"seoDescription":259,"stem":260,"subtopic":261,"topic":19,"topicSlug":21,"updated":68,"__hash__":262},"qa\u002Fpython\u002Foop\u002Fabc-protocols.md","Abc Protocols",{"type":29,"value":231,"toc":232},[],{"title":32,"searchDepth":33,"depth":33,"links":233},[],{},6,"\u002Fpython\u002Foop\u002Fabc-protocols",[238,242,246,250,254],{"id":239,"difficulty":60,"q":240,"a":241},"what-is-abc","What is an abstract base class and what does @abstractmethod do?","An **abstract base class (ABC)** is a class that **can't be instantiated\ndirectly** and defines a set of methods subclasses **must implement**. You build\none by subclassing `abc.ABC` and decorating required methods with\n**`@abstractmethod`**. Python refuses to instantiate any subclass that leaves an\nabstract method unimplemented.\n\n```python\nfrom abc import ABC, abstractmethod\n\nclass Shape(ABC):\n    @abstractmethod\n    def area(self):            # subclasses MUST implement this\n        ...\n\nShape()                        # TypeError — can't instantiate abstract class\n\nclass Circle(Shape):\n    def __init__(self, r):\n        self.r = r\n    def area(self):            # concrete implementation\n        return 3.14159 * self.r ** 2\n\nCircle(2).area()               # 12.566... — works\n```\n\n`@abstractmethod` turns the \"must implement\" contract into an **enforced,\nload-time check** rather than a runtime `NotImplementedError` later. It defines\nan interface that subclasses are required to fulfill.\n",{"id":243,"difficulty":60,"q":244,"a":245},"why-use-abcs","Why use ABCs instead of just regular classes?","ABCs **enforce an interface** — they guarantee at instantiation time that\nsubclasses provide the required methods, catching mistakes **early** instead of\nblowing up deep in your code. They also document intent clearly and integrate\nwith `isinstance` checks, so callers can verify capabilities.\n\n```python\nfrom abc import ABC, abstractmethod\n\nclass Storage(ABC):\n    @abstractmethod\n    def save(self, key, value): ...\n    @abstractmethod\n    def load(self, key): ...\n\nclass FileStorage(Storage):\n    def save(self, key, value): ...\n    # forgot load() ...\n\nFileStorage()      # TypeError — flags the missing method immediately\nisinstance(FileStorage, type) and issubclass(FileStorage, Storage)  # True\n```\n\nUse an ABC when you have a **family of classes that must share a contract** and\nyou want that contract enforced. For looser, optional interfaces, plain duck\ntyping or a `Protocol` may fit better.\n",{"id":247,"difficulty":35,"q":248,"a":249},"collections-abc","What is collections.abc and how is it useful?","`collections.abc` provides the **standard ABCs for Python's container\nprotocols** — `Iterable`, `Iterator`, `Sequence`, `Mapping`, `Hashable`, and\nmore. You use them two ways: as a **base class** to inherit mixin methods, and\nin **`isinstance` checks** to test whether an object supports a protocol.\n\n```python\nfrom collections.abc import Iterable, Sequence, Mapping\n\nisinstance([1, 2], Iterable)   # True\nisinstance(\"abc\", Sequence)    # True\nisinstance({}, Mapping)        # True\n\nclass MyList(Sequence):        # inherit the protocol's mixin methods\n    def __init__(self, data):\n        self._data = data\n    def __getitem__(self, i):\n        return self._data[i]\n    def __len__(self):\n        return len(self._data)\n    # get __contains__, __iter__, __reversed__, index, count for FREE\n```\n\nSubclassing `Sequence` and implementing just `__getitem__` + `__len__` gives you\na fully functional sequence. Prefer these standard ABCs over inventing your own\nfor container-like types.\n",{"id":251,"difficulty":60,"q":252,"a":253},"duck-typing-vs-abcs","What is the difference between duck typing and ABCs?","**Duck typing** is \"if it walks like a duck and quacks like a duck, it's a\nduck\" — Python doesn't check the type, it just **tries the operation** and works\nif the needed methods exist. **ABCs add an explicit, enforced contract** on top,\nletting you assert membership and catch missing methods up front.\n\n```python\n# Duck typing — no declared interface, just call and hope:\ndef make_it_quack(thing):\n    thing.quack()          # works for ANYTHING with a quack() method\n\nclass Dog:\n    def quack(self): return \"woof-quack\"\nmake_it_quack(Dog())       # works — Dog \"is\" a duck here\n\n# ABC — explicit, checkable contract:\nfrom abc import ABC, abstractmethod\nclass Duck(ABC):\n    @abstractmethod\n    def quack(self): ...\n```\n\nDuck typing is flexible and Pythonic but defers errors to runtime; ABCs trade\nsome flexibility for **early enforcement and clear interfaces**. Use duck typing\nfor loose coupling, ABCs when you want guarantees.\n",{"id":255,"difficulty":35,"q":256,"a":257},"typing-protocol","What is typing.Protocol and how does it relate to duck typing?","`typing.Protocol` enables **structural typing** (\"static duck typing\"): a class\nsatisfies a protocol simply by **having the right methods\u002Fattributes** — no\nexplicit inheritance needed. Type checkers verify the structure **statically**,\ngiving you duck typing's flexibility **with** static safety. Add\n**`@runtime_checkable`** to also allow `isinstance` checks at runtime.\n\n```python\nfrom typing import Protocol, runtime_checkable\n\n@runtime_checkable\nclass Drawable(Protocol):\n    def draw(self) -> str: ...     # required shape, not inheritance\n\nclass Button:                      # does NOT inherit Drawable\n    def draw(self) -> str:\n        return \"[Button]\"\n\ndef render(item: Drawable) -> str: # type checker accepts Button\n    return item.draw()\n\nrender(Button())                   # works — structural match\nisinstance(Button(), Drawable)     # True — thanks to @runtime_checkable\n```\n\nUnlike ABCs, classes don't register or subclass anything — matching the\n**structure is enough**. Note `@runtime_checkable` only checks method\n**names\u002Fexistence**, not signatures. Use Protocols for flexible, statically\nverified interfaces; use ABCs when you need shared implementation or explicit\nregistration.\n",{"description":32},"Python interview questions on abstract base classes and @abstractmethod, why use ABCs, collections.abc, duck typing vs ABCs, and typing.Protocol for structural\u002Fstatic duck typing with runtime_checkable.","python\u002Foop\u002Fabc-protocols","Abstract Base Classes & Protocols","ND9wrQMm980ybmmy5dud6WKKaNzHF-LWIzfyLRcQJ5Q",1781808675006]