Descriptors: __get__, __set__, @property, and __slots__ Memory Savings
- Last updated
- Prerequisites:
- Python Classes and Attributes
- Python Data Model
- python
- descriptors
- property
- slots
- memory-optimization
- attribute-lookup
Read at your depth
The practical view
A Descriptor is an object attribute with binding behavior, defined by implementing `__get__`, `__set__`, or `__delete__`. `@property` is the most common built-in descriptor. Adding `__slots__ = ('x', 'y')` to a class tells Python not to create an instance `__dict__` or `__weakref__`, storing attributes in a compact C-level array and reducing per-instance memory consumption by 60-70%.
The same idea in other frameworks
`__slots__ = ('name', 'age')` and Descriptor protocol (`__get__`/`__set__`).C# Auto-properties with backing fields (`public string Name { get; set; }`).Rust struct fields with contiguous memory layout (zero dictionary overhead).C++ class member memory offsets.Legacy vs modern
Memory-Heavy Instance __dict__ vs Compact __slots__
1,000,000 standard instances consume hundreds of megabytes of `__dict__` memory; `__slots__` cuts RAM usage by ~70%.
class Point: def __init__(self, x, y): self.x = x; self.y = y // ❌ 150 bytes per instance__slots__ = ('x', 'y') // ✅ NEW: Eliminates __dict__, saves 70% RAMInterview gotchas
Context
Interviewers test your knowledge of inheritance rules with `__slots__`.
The mechanical answer
If a subclass does not define its own `__slots__`, **it will create an instance `__dict__` anyway**, completely defeating the memory optimization for instances of the subclass. To preserve memory savings down an inheritance chain, **every subclass must explicitly declare `__slots__ = ()`** (or specify its additional slot attributes).
Trap
Inheriting from a class with `__slots__` without declaring `__slots__` in the child silently restores `__dict__` allocation.
Context
Checks mastery of Python's exact attribute lookup resolution order.
The mechanical answer
Because of Python's strict attribute resolution priority implemented in `object.__getattribute__`. Data Descriptors define both `__get__` and `__set__`. Python checks Data Descriptors on the class *first* before checking the instance `__dict__`. This guarantees that properties with setters (`@property`) cannot be accidentally shadowed or bypassed by directly setting `obj.__dict__['prop'] = val`.
Trap
Non-data descriptors (methods without setters) are checked *after* `__dict__`, which is why monkey-patching an instance method `obj.method = custom_fn` successfully overrides the class method.