31.6 Slot Descriptors and __slots__

While descriptors provide a powerful mechanism for managing attribute access, Python classes inherently rely on a dictionary (__dict__) to store instance attributes. This offers tremendous flexibility but comes with a memory cost: every instance must allocate a dictionary, which can be significant when creating many small objects. The __slots__ class variable offers a high-performance alternative by fundamentally changing how instances store their data. How slots Replaces dict When you define __slots__ in a class, you are instructing the Python interpreter to create a fixed set of names (slots) for attributes on each instance. Instead of a dynamic dictionary, the interpreter reserves space for a small, fixed-length array (a C-style struct) within each instance to hold the values for these predefined attributes. This transformation happens deep within the object creation machinery.

31.5 Descriptors in the Standard Library: classmethod, staticmethod

While property is the most common descriptor encountered by Python developers, the language’s standard library includes two other critical descriptors that are fundamental to class design: classmethod and staticmethod. These built-in types are implemented as descriptors to provide a clean, consistent API for defining methods that do not operate on a specific instance. The classmethod Descriptor A classmethod is a method that receives the class itself (cls) as its implicit first argument, rather than an instance (self). This makes it ideal for factory methods, alternative constructors, or any method that needs to operate at the class level, potentially accessing or modifying class state.

31.4 Writing a Reusable Validator Descriptor

While Python’s property decorator is excellent for adding managed access to a single attribute in a single class, it lacks reusability. If you need the same validation logic across multiple attributes or multiple classes, copying and pasting the property definition is a maintenance nightmare. This is where writing a descriptor, specifically a reusable validator descriptor, becomes a powerful tool. A descriptor is a class that implements at least one of the __get__, __set__, or __delete__ methods. These methods are the “machinery” that properties are built upon, and by creating your own, you can encapsulate validation logic into a single, reusable object.

31.3 How @property Is Implemented Using Descriptors

While the @property decorator provides an elegant, high-level interface for creating managed attributes, its implementation rests squarely on the more fundamental concept of descriptors. Understanding this machinery is crucial for any Python developer who wishes to move beyond merely using properties to extending and customizing the behavior of object attribute access. The Descriptor Protocol: A Primer At its core, a descriptor is any object that defines at least one of the methods in the descriptor protocol: __get__(), __set__(), or __delete__(). These methods are hooks that are automatically called by Python’s internal attribute access machinery when a descriptor is accessed as an attribute on another object. The __get__ method is called when the attribute is retrieved (instance.descriptor), __set__ when it’s assigned (instance.descriptor = value), and __delete__ when it’s deleted (del instance.descriptor). A descriptor that only defines __get__ is termed a non-data descriptor, while one that defines __set__ or __delete__ is a data descriptor. This distinction is critical, as data descriptors have precedence over an instance’s dictionary, which is why properties can override instance attributes.

31.2 Data Descriptors vs Non-Data Descriptors

In Python, descriptors are a powerful mechanism that underpins properties, methods, and class-level functionality. The distinction between data descriptors and non-data descriptors is fundamental to understanding attribute lookup precedence and is crucial for designing robust and predictable classes. The Defining Difference: __set__ or __delete__ The official Python documentation defines a descriptor as any object that implements at least one of the three special methods: __get__(), __set__(), or __delete__(). This single criterion creates the primary classification:

31.1 The Descriptor Protocol: __get__, __set__, __delete__, __set_name__

Descriptors are a core mechanism in Python’s object system, enabling the powerful property, method, and class method functionality we often take for granted. They provide a protocol for overriding default attribute access behavior (getting, setting, and deleting) on a per-attribute basis. Any object that defines at least one of the methods __get__, __set__, or __delete__ is considered a descriptor. This protocol is the foundational machinery that makes the @property decorator work.

— joke —

...