Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Impara The Attribute Lookup Chain | What Descriptors Are
Python Descriptors Explained

The Attribute Lookup Chain

Scorri per mostrare il menu

Before understanding descriptors, you need to understand how Python finds an attribute when you write obj.name. The answer is not as simple as "look it up in the object's dictionary." Python follows a specific chain of lookups – and descriptors are what make that chain powerful.

The Lookup Order

When you access obj.attr, Python follows this sequence:

  • the type's MRO (method resolution order) is searched first for a data descriptor on the class or its parents;
  • if no data descriptor is found, the instance's __dict__ is checked;
  • if not found there, the type's MRO is searched again for a non-data descriptor or plain class attribute;
  • if none of the above match, AttributeError is raised.
1234567891011121314
# Observing the basic lookup chain class Product: category = "electronics" # Class attribute def __init__(self, name, price): self.name = name # Instance attribute stored in __dict__ self.price = price laptop = Product("ThinkPad", 1200.0) print(laptop.name) # Found in instance __dict__ print(laptop.category) # Not in __dict__ – found on the class print(laptop.__dict__) # {'name': 'ThinkPad', 'price': 1200.0} print(Product.__dict__.keys()) # Contains 'category', '__init__', etc.

__dict__ at the Instance and Class Level

Every regular instance has a __dict__ that stores its attributes. The class also has a __dict__ that stores methods and class-level attributes:

1234567891011121314151617
# Comparing instance and class __dict__ class Invoice: tax_rate = 0.2 # Class attribute – stored in Invoice.__dict__ def __init__(self, invoice_id, amount): self.invoice_id = invoice_id # Instance attribute self.amount = amount invoice = Invoice("INV-001", 5000.0) print(invoice.__dict__) # {'invoice_id': 'INV-001', 'amount': 5000.0} print(Invoice.__dict__["tax_rate"]) # 0.2 # Instance attribute shadows class attribute with the same name invoice.tax_rate = 0.15 # Creates a new entry in instance __dict__ print(invoice.tax_rate) # 0.15 – instance __dict__ found first print(Invoice.tax_rate) # 0.2 – class attribute unchanged

How type.__getattribute__ Drives the Lookup

The entire lookup process is implemented in type.__getattribute__. You rarely call it directly, but understanding that it exists explains why descriptors work – they are objects the __getattribute__ machinery knows how to invoke:

123456789101112
# Explicit lookup through __getattribute__ class Config: debug = False def __init__(self, env): self.env = env config = Config("production") # These two lines do the same thing print(config.env) print(object.__getattribute__(config, "env"))

When Instance Attributes Shadow Class Attributes

Instance attributes always shadow class attributes of the same name – unless the class attribute is a data descriptor (covered in Chapter 3). This distinction is the key to understanding why descriptors exist:

123456789101112
# Instance attribute shadowing a class attribute class Report: status = "pending" report = Report() print(report.status) # "pending" – from class report.status = "approved" # Written to instance __dict__ print(report.status) # "approved" – instance shadows class del report.status # Removing the instance attribute print(report.status) # "pending" – class attribute visible again
question mark

In Python's attribute lookup chain, what is checked before the instance's __dict__?

Seleziona la risposta corretta

Tutto è chiaro?

Come possiamo migliorarlo?

Grazie per i tuoi commenti!

Sezione 1. Capitolo 1

Chieda ad AI

expand

Chieda ad AI

ChatGPT

Chieda pure quello che desidera o provi una delle domande suggerite per iniziare la nostra conversazione

Sezione 1. Capitolo 1
some-alt