Descriptors, decorators, context managers and garbage collection
The weak reference lets you ask 'is it still alive?' without keeping the object alive.
How Python's object model really works, the way senior interviews probe it: method resolution order, descriptors, __new__ versus __init__, the GIL, garbage collection, decorators, generators versus iterators, __slots__ and context managers.
Change the code and press Run (Ctrl+Enter). Predict what will happen first, then break it on purpose and read what changes. Your edits are saved and match the lesson page.
Ready for more? This phase has 6 hands-on exercises with automatic checks.
Practice questions
Write the code in the editor, run it, then open the model answer to compare.
Write a context manager class called Collector with __enter__ returning a list and __exit__ that prints how many items were collected, even when an error happens inside the with.
Write a decorator count_calls that counts how many times a function was called and exposes the number as fn.calls, keeping the original name.
Create a class Meters(float) using __new__ so that Meters(3) is a float subclass instance that prints as 3.0 m when you call str() on it.
Explain it without notes
Explain MRO and what super() does in a diamond. Give the MRO of class D(B, C) where both extend A.
What is the difference between __new__ and __init__? When would you override __new__?
What is a descriptor? Name three built-in features that are implemented with descriptors.
What does the GIL do, and when do threads still help? What are your options for CPU-bound work?
How does CPython free memory? Why can a reference cycle survive del?
What is the difference between an instance method, a @classmethod and a @staticmethod? When is each the right choice?
What happens to hashing when a class defines __eq__? Why does that matter for sets and dict keys?
Expected output
ValueError: must be positive
price still: 5
calling add
5 | add | Add two numbers.
open db
close db
caught: boom
refcount hit zero, freed at once: True
cycle still alive: True
after gc.collect(): True
iterator is its own iterator: True