OOP and Classes
What Python does when you write `class`: two-step construction, C3 method resolution, the dunder protocols, dataclasses, descriptors and metaclasses. Interviewers use it to find the edge of your object model.
part ofPythonoverview, primer and where to startread it →on this pageshowhide
explore
- Construction and Attributes19 questions
- Object Construction: __new__ vs __init__4 questions
- Instance, Class and Static Methods3 questions
- Class vs Instance Attributes4 questions
- Name Mangling and Privacy4 questions
- Finalization and Weak References4 questions
- Inheritance and the MRO20 questions
- super() and Cooperative Inheritance4 questions
- MRO and C3 Linearization4 questions
- Multiple Inheritance and Mixins4 questions
- __init_subclass__ and Subclass Hooks4 questions
- Subclassing Built-in Types4 questions
- Dunder Protocols22 questions
- __repr__ vs __str__4 questions
- __eq__ and __hash__ Contract4 questions
- Container and Sequence Protocols4 questions
- Operator Overloading and __call__4 questions
- Reflected Operands and NotImplemented3 questions
- Implicit Special Method Lookup3 questions
- Dataclasses and Value Types16 questions
- Generated __init__ and field()4 questions
- Dataclass Options: frozen, order, slots4 questions
- Tuple-Backed Record Types4 questions
- __post_init__ and InitVar4 questions
- Descriptors and Attribute Access16 questions
- property and Computed Attributes4 questions
- The Descriptor Protocol4 questions
- __getattr__ vs __getattribute__4 questions
- cached_property and Its Cost4 questions
- Metaclasses, ABCs and Class Machinery12 questions
- type and Metaclasses4 questions
- Abstract Base Classes4 questions
- __slots__ Mechanics4 questions
- Symbolic Constant Types11 questions
- Enum, IntEnum and StrEnum4 questions
- Flag and Bitwise Members4 questions
- Aliases, unique and _missing_3 questions
- Copy and Pickle Hooks12 questions
- __getstate__ and __setstate__4 questions
- __reduce__ and copyreg4 questions
- __copy__ and __deepcopy__4 questions
questions
128 · 8 sectionsWhat is the difference between a class attribute and an instance attribute in Python?
basics
~20 sA class attribute is created in the class body and stored on the class, so every instance sees the same object. An instance attribute is created by assigning to self.name and lives in that one object's own namespace.
How do a plain method, a @classmethod and a @staticmethod differ in Python?
basics
~20 sA plain method takes the instance as its first argument, self. A classmethod takes the class as cls, so a subclass gets its own class. A staticmethod receives nothing implicitly; it is just a function stored in the class namespace.
In Python, what does `_name` mean versus `__name` on a class attribute?
basics
~20 sA single leading underscore is pure convention: it marks an attribute as internal and the interpreter ignores it. A double leading underscore inside a class body is rewritten by the compiler to _ClassName__name, which avoids subclass collisions rather than blocking access.
What is the difference between `weakref.ref` and `weakref.proxy` in Python?
basics
~20 sBoth refer to an object without keeping it alive. A weakref.ref is a callable: call it to get the object, or None once it is gone. A weakref.proxy forwards attribute access through, and raises ReferenceError once it is gone.
What is a mixin class in Python, and how does it differ from a base class?
basics
~20 sA mixin is a small class that contributes methods to other classes through multiple inheritance and is never instantiated on its own. A base class says what an object is; a mixin only adds a slice of behaviour.
In Python, if `class D(B, C)` and both B and C override `run()`, which one executes?
basics
~10 sB's run executes. Python flattens the hierarchy into one method resolution order — D, B, C, then their shared base, then object — and calls the first class in that list that defines run.
What does a zero-argument super() call inside a method resolve to at runtime?
basics
~10 sA zero-argument super() builds a proxy that starts attribute lookup at the class after the one where the method was defined, following the receiver's method resolution order rather than the class's declared base.
Why does d.copy() on a dict subclass return a plain dict while copy.copy(d) does not?
basics
~20 sdict.copy(), dict(d) and {**d} are built-in constructors: they allocate an exact dict and copy the items, so your class and its instance attributes are lost. copy.copy(d) goes through the reduction protocol and rebuilds an instance of your subclass.
When does Python call a base class's `__init_subclass__`, and what is `cls` bound to inside it?
basics
~20 sPython calls init_subclass on the nearest parent every time a new subclass of it is created, passing the brand-new subclass as cls. It is implicitly a classmethod, and it never runs for the class that defines it.
Which dunder methods make a custom class work with len(), obj[i] and `in`?
basics
~20 slen answers len(obj), getitem answers obj[i] and slicing, and contains answers x in obj. Defining them on any class makes the built-ins work, because Python looks for the methods, not for a particular base class.
What is the difference between `__repr__` and `__str__` on a Python class?
basics
~10 s__repr__ returns the unambiguous developer-facing view, used by repr(), the REPL and debuggers. __str__ returns the readable user-facing view used by print() and str(). A missing __str__ falls back to __repr__, never the reverse.
What contract must `__hash__` satisfy relative to `__eq__` in Python?
basics
~10 sObjects that compare equal must return equal hash values, and an object's hash must not change while it lives. The converse is not required: equal hashes are a collision, resolved by ==.
Why does defining `__eq__` make a class's instances unusable as dict keys?
basics
~10 sWhen a class body defines eq without hash, Python sets hash to None in that class, so hash() raises TypeError: unhashable type. Define hash over the same fields to restore it.
Why does assigning `obj.__len__ = lambda: 5` leave `len(obj)` raising TypeError?
basics
~20 sBecause len() resolves len on type(obj), not on the object. The lambda lands in the instance dictionary, which implicit special method lookup never reads, so the class still has no len and len() raises TypeError.
In a typing.NamedTuple class, how do you give fields defaults and add methods?
basics
~20 sWrite the record as a class: an annotated name with an assigned value becomes that field's default, and ordinary def statements become real methods on the generated tuple subclass. Defaulted fields must come last, exactly as with function parameters.
What does the @dataclass decorator generate, and which class attributes become fields?
basics
~10 s@dataclass writes init, repr and eq from the class's annotated attributes, in declaration order. Only annotated names become fields; a plain assignment with no annotation stays an ordinary class attribute.
What does @dataclass(frozen=True) actually prevent, and what does it not?
basics
~20 sSetting or deleting an attribute on a frozen dataclass instance raises dataclasses.FrozenInstanceError, because the decorator generates blocking setattr and delattr methods. It does not freeze what the fields point at: a list field can still be mutated in place.
When does __post_init__ run in a Python dataclass, and what is it for?
basics
~10 s@dataclass generates an init that assigns every field, and if the class defines post_init that generated constructor calls it as its final statement. It is the hook for validating arguments and computing derived values.
Why does @dataclass raise ValueError for `tags: list = []`, and what is the fix?
basics
~10 sA default in the class body is created once and would be shared by every instance, so @dataclass rejects it at class-creation time. Use dataclasses.field(default_factory=list), which calls the factory per instance.
What does Python's @property decorator do, and how do you add a setter to it?
basics
~20 sThe @property decorator turns a method into a read-only attribute, so callers write obj.title rather than obj.title(). Decorating a second method of the same name with @title.setter makes assignment work and gives you one place to validate the value.
Why does `self._data = value` inside `__setattr__` recurse forever, and what is the fix?
basics
~20 sBecause an assignment inside setattr is itself an attribute assignment, so it calls setattr again and never terminates, ending in RecursionError. Break the cycle by delegating to object.setattr or super().setattr, or by writing straight into the instance dictionary.
How does `__getattribute__` differ from `__getattr__` on a Python class?
basics
~20 sgetattribute is called for every attribute access on an instance and performs the whole lookup itself; getattr is only the fallback the runtime calls when that lookup fails with AttributeError. Overriding getattribute touches every access and every internal read.
How does functools.cached_property make every read after the first skip the descriptor?
basics
~20 sIt is a non-data descriptor: the first read runs the function and stores the result in the instance __dict__ under the same attribute name. Later reads find that entry first, so the descriptor never runs again.
How do data and non-data descriptors differ in Python's attribute lookup order?
basics
~20 sA data descriptor defines set or delete and outranks the instance dict; a non-data descriptor defines only get and loses to it. So instance data can shadow a non-data descriptor but never a data one.
Why does instantiating a subclass of abc.ABC that leaves an @abstractmethod unimplemented raise TypeError?
basics
~20 sabc.ABCMeta collects every still-abstract name into a frozenset stored on the class as abstractmethods, and object construction refuses to run while that set is non-empty. The failure therefore lands at instantiation, not at class definition.
What does defining `__slots__` on a Python class change about its instances?
basics
~10 sInstances lose their per-instance __dict__. Only the names listed in __slots__ can be assigned, each into a fixed storage slot, and assigning any other attribute raises AttributeError instead of silently creating it.
What does the three-argument `type(name, bases, namespace)` call create in Python?
basics
~10 sIt creates and returns a brand-new class object at runtime. type('Duck', (Bird,), {'legs': 2}) produces the same kind of object a class Duck(Bird) statement would. The one-argument form, type(obj), instead reports an object's class.
How must @abc.abstractmethod be stacked with @property or @classmethod, and why?
basics
~10 sPut @abstractmethod innermost, directly above the def, with @property, @classmethod or @staticmethod outside it. The reverse order raises AttributeError while the class body runs, because those wrapper objects expose a read-only isabstractmethod.
When a class uses `metaclass=Meta`, in what order do `__prepare__`, `__new__` and `__init__` run?
basics
~20 sMeta.__prepare__ runs first and returns the mapping the class body executes into. The body then runs, and the filled mapping is handed to Meta.__new__, which creates the class object, and finally to Meta.__init__, which configures it.
How do you combine enum.Flag members with | and test one with &?
basics
~20 sThe | operator unions two enum.Flag members into one composite member carrying both bits, and & intersects them. Test a single flag with perms & Perm.READ, which is truthy only when that bit is set.
How do enum.Enum members compare with `is` and `==`, and why?
basics
~20 sEach enum.Enum member is created once and cached, so every access returns the same object and is compares them exactly. A plain member equals only itself: Color.RED == 1 is False, and so is a same-valued member of another enum.
Why does an Enum member defined with a duplicate value never appear in iteration?
basics
~20 sA duplicate value creates an alias, not a new member: enum.Enum binds the second name to the member that already owns that value. Iterating the class yields only canonical members, so the alias name never shows up.
When should you use enum.IntEnum or enum.StrEnum instead of a plain enum.Enum?
basics
~20 sOnly when a member must still work as a raw int or string somewhere you cannot change - a wire format, a stored column, a C constant. Those members really are int and str subclasses: a compatibility shim, not a default.
What values does enum.auto() assign inside an enum.Flag subclass?
basics
~20 sInside a Flag or IntFlag subclass, auto() assigns successive powers of two — 1, 2, 4, 8 — rather than the 1, 2, 3 it gives a plain Enum, so every member owns a distinct bit and members can be combined.
How does a class control what `copy.copy()` and `copy.deepcopy()` return?
basics
~10 sDefine copy(self) for copy.copy() and deepcopy(self, memo) for copy.deepcopy(). Each returns the finished new object, so the class itself decides which attributes are duplicated, which are shared, and which are rebuilt.
What must a class's `__reduce__` return, and how does `pickle` use it to rebuild the object?
basics
~20 sreduce returns either a string naming a module-level global to look up, or a tuple whose first item is a callable and whose second is a tuple of arguments. Loading imports that callable by name and calls it with those arguments.
How do __getstate__ and __setstate__ let you pickle an object holding a threading.Lock?
basics
~10 sgetstate returns a copy of the instance dict with the lock key removed, so it is never serialised. setstate merges the stored data back and creates a fresh threading.Lock on the restored object.
Why does `pickle.dumps` reject a lambda but accept a module-level function?
basics
~20 spickle does not serialise a function's code. It stores the function's module name and qualified name and looks that pair up again on load. A lambda's qualified name is <lambda>, which resolves to nothing, so dumping raises pickle.PicklingError.
What do __getstate__ and __setstate__ do when Python pickles and unpickles an object?
basics
~10 sgetstate returns the data written into the pickle, which by default is the instance dict. setstate receives that data on unpickling and rebuilds the object from it. Unpickling never calls init.