Questions
14 of 19
1How would you make a custom class usable in a for loop?
2How do __enter__ and __exit__ work together to implement the context manager protocol?
3How does Python implement inheritance, and what is the Method Resolution Order (MRO)?
4What are dataclasses, and how do they reduce boilerplate compared to manually writing __init__, __eq__, and __repr__?
5What is the @property decorator, and what problem does it solve compared to plain getter/setter methods?
6What is the difference between method overriding and method overloading, and why doesn't Python natively support overloading?
7What problem does the diamond inheritance pattern cause, and how does Python's MRO resolve it?
8What is the difference between a class and an instance in Python?
9How does Python implement encapsulation given that it has no true 'private' access modifiers?
10What is the purpose of self in instance methods, and why must it be explicitly declared in Python?
11What is the difference between __str__ and __repr__, and what convention should __repr__ follow?
12What is a metaclass, and what is type's relationship to every class in Python?
13What is the purpose of __eq__ and __hash__, and why must they be implemented consistently?
14What is a descriptor, and how do @property and ORMs like Django's models rely on the descriptor protocol?
15What is the difference between an instance method, a class method, and a static method?
16What is composition, and why is 'favor composition over inheritance' often recommended?
17What are dunder (magic) methods, and how do they enable operator overloading?
18When would you use a metaclass instead of a class decorator or __init_subclass__?
19How do abstract base classes (abc.ABC) enforce interface contracts in Python?
14 / 19

What is a descriptor, and how do @property and ORMs like Django's models rely on the descriptor protocol?

Difficulty: 9/10
Descriptors, Properties, ORMs

A descriptor is an object defining get/set/delete and intercepting attribute access

A descriptor is any object that defines get, and optionally set or delete. When stored as a class attribute, Python calls it instead of returning the object itself when the attribute is accessed. Data descriptors define set or delete and take precedence over the instance dict; non-data descriptors define only get and can be shadowed by an instance attribute. This is the mechanism behind functions becoming bound methods, property, classmethod, staticmethod, and slot. ORMs like Django use descriptors to map Python attributes to database columns: accessing a field on an instance returns the loaded value, and assigning to it marks the field dirty for later flush.

  1. 1

    data descriptor: get plus set or delete; instance dict cannot shadow it.

  2. 2

    non-data descriptor: get only; instance attributes can shadow it, which is why overriding a method by setting an instance attribute works.

  3. 3

    Functions are non-data descriptors, which is exactly how method binding is implemented.

  4. 4

    set_name lets a descriptor learn its attribute name at class creation time, useful for ORMs.

  5. 5

    Trade-off: descriptors are powerful but subtle. A plain property covers most cases; reach for a custom descriptor only when you need reuse across many attributes.

  6. 6

    Common mistake: forgetting that descriptor get is called with None as the instance when accessed on the class.

  7. 7

    Version note: set_name was added in 3.6. The overall descriptor protocol has been stable since Python 2.2.

javascript

Scenario Questions

0-2 years experience

  1. 1Why does assigning obj.method = some_function not permanently break method binding?
  2. 2Which three methods make up the descriptor protocol?

2-5 years experience

  1. 1You write a descriptor with only __get__ and set an instance attribute with the same name. What happens?
  2. 2How does property differ from a custom descriptor in practice?

5-8 years experience

  1. 1You need a reusable validated field across dozens of model attributes. How do you implement it with descriptors and __set_name__?
  2. 2You are implementing lazy loading for a related object. How do you design the descriptor and cache the loaded value?

8+ years experience

  1. 1Design a mini-ORM descriptor layer that supports validation, lazy loading, dirty tracking, and bulk updates with minimal per-access overhead.
  2. 2Explain how descriptors interact with slots, __init_subclass__, and metaclasses in a framework like Django, and where performance bottlenecks arise.

Follow-up Questions

  • Why does a data descriptor take precedence over the instance __dict__?
  • How would you implement a lazy-loaded relationship field with descriptors?
Share

Share via WhatsApp, X, Facebook, LinkedIn or copy link. Open Graph preview enabled.