blob: ab5a573c6a06d10b704baeaeea6efc9a531317b7 [file] [log] [blame]
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -07001.. _descriptorhowto:
2
Georg Brandl45cceeb2010-05-19 21:39:51 +00003======================
4Descriptor HowTo Guide
5======================
6
7:Author: Raymond Hettinger
8:Contact: <python at rcn dot com>
9
10.. Contents::
11
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -070012
13:term:`Descriptors <descriptor>` let objects customize attribute lookup,
14storage, and deletion.
15
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -070016This guide has four major sections:
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -070017
181) The "primer" gives a basic overview, moving gently from simple examples,
Miss Islington (bot)ec0788c2020-11-15 18:27:56 -080019 adding one feature at a time. Start here if you're new to descriptors.
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -070020
212) The second section shows a complete, practical descriptor example. If you
22 already know the basics, start there.
23
243) The third section provides a more technical tutorial that goes into the
25 detailed mechanics of how descriptors work. Most people don't need this
26 level of detail.
27
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -0700284) The last section has pure Python equivalents for built-in descriptors that
29 are written in C. Read this if you're curious about how functions turn
Miss Skeleton (bot)27c72ba2020-11-01 20:55:57 -080030 into bound methods or about the implementation of common tools like
31 :func:`classmethod`, :func:`staticmethod`, :func:`property`, and
32 :term:`__slots__`.
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -070033
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -070034
35Primer
36^^^^^^
37
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -070038In this primer, we start with the most basic possible example and then we'll
39add new capabilities one by one.
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -070040
41
42Simple example: A descriptor that returns a constant
43----------------------------------------------------
44
Miss Islington (bot)ec0788c2020-11-15 18:27:56 -080045The :class:`Ten` class is a descriptor that always returns the constant ``10``
Miss Islington (bot)543724b2020-11-24 22:47:17 -080046from its :meth:`__get__` method:
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -070047
Miss Islington (bot)543724b2020-11-24 22:47:17 -080048.. testcode::
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -070049
50 class Ten:
51 def __get__(self, obj, objtype=None):
52 return 10
53
Miss Islington (bot)543724b2020-11-24 22:47:17 -080054To use the descriptor, it must be stored as a class variable in another class:
55
56.. testcode::
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -070057
58 class A:
59 x = 5 # Regular class attribute
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -080060 y = Ten() # Descriptor instance
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -070061
62An interactive session shows the difference between normal attribute lookup
Miss Islington (bot)543724b2020-11-24 22:47:17 -080063and descriptor lookup:
64
65.. doctest::
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -070066
67 >>> a = A() # Make an instance of class A
68 >>> a.x # Normal attribute lookup
69 5
70 >>> a.y # Descriptor lookup
71 10
72
Miss Islington (bot)ec0788c2020-11-15 18:27:56 -080073In the ``a.x`` attribute lookup, the dot operator finds the key ``x`` and the
74value ``5`` in the class dictionary. In the ``a.y`` lookup, the dot operator
75finds a descriptor instance, recognized by its ``__get__`` method, and calls
76that method which returns ``10``.
77
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -070078Note that the value ``10`` is not stored in either the class dictionary or the
79instance dictionary. Instead, the value ``10`` is computed on demand.
80
81This example shows how a simple descriptor works, but it isn't very useful.
82For retrieving constants, normal attribute lookup would be better.
83
84In the next section, we'll create something more useful, a dynamic lookup.
85
86
87Dynamic lookups
88---------------
89
Miss Islington (bot)ec0788c2020-11-15 18:27:56 -080090Interesting descriptors typically run computations instead of returning
Miss Islington (bot)543724b2020-11-24 22:47:17 -080091constants:
92
93.. testcode::
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -070094
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -070095 import os
96
97 class DirectorySize:
98
99 def __get__(self, obj, objtype=None):
100 return len(os.listdir(obj.dirname))
101
102 class Directory:
103
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800104 size = DirectorySize() # Descriptor instance
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700105
106 def __init__(self, dirname):
107 self.dirname = dirname # Regular instance attribute
108
109An interactive session shows that the lookup is dynamic — it computes
110different, updated answers each time::
111
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700112 >>> s = Directory('songs')
Miss Islington (bot)ec0788c2020-11-15 18:27:56 -0800113 >>> g = Directory('games')
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700114 >>> s.size # The songs directory has twenty files
115 20
Miss Islington (bot)ec0788c2020-11-15 18:27:56 -0800116 >>> g.size # The games directory has three files
117 3
118 >>> open('games/newfile').close() # Add a fourth file to the directory
119 >>> g.size # File count is automatically updated
120 4
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700121
122Besides showing how descriptors can run computations, this example also
123reveals the purpose of the parameters to :meth:`__get__`. The *self*
124parameter is *size*, an instance of *DirectorySize*. The *obj* parameter is
Raymond Hettinger4c239a32020-11-06 16:46:10 -0800125either *g* or *s*, an instance of *Directory*. It is the *obj* parameter that
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700126lets the :meth:`__get__` method learn the target directory. The *objtype*
127parameter is the class *Directory*.
128
129
130Managed attributes
131------------------
132
133A popular use for descriptors is managing access to instance data. The
134descriptor is assigned to a public attribute in the class dictionary while the
135actual data is stored as a private attribute in the instance dictionary. The
136descriptor's :meth:`__get__` and :meth:`__set__` methods are triggered when
137the public attribute is accessed.
138
139In the following example, *age* is the public attribute and *_age* is the
140private attribute. When the public attribute is accessed, the descriptor logs
Miss Islington (bot)543724b2020-11-24 22:47:17 -0800141the lookup or update:
142
143.. testcode::
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700144
145 import logging
146
147 logging.basicConfig(level=logging.INFO)
148
149 class LoggedAgeAccess:
150
151 def __get__(self, obj, objtype=None):
152 value = obj._age
153 logging.info('Accessing %r giving %r', 'age', value)
154 return value
155
156 def __set__(self, obj, value):
157 logging.info('Updating %r to %r', 'age', value)
158 obj._age = value
159
160 class Person:
161
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800162 age = LoggedAgeAccess() # Descriptor instance
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700163
164 def __init__(self, name, age):
165 self.name = name # Regular instance attribute
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800166 self.age = age # Calls __set__()
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700167
168 def birthday(self):
169 self.age += 1 # Calls both __get__() and __set__()
170
171
172An interactive session shows that all access to the managed attribute *age* is
Miss Islington (bot)0aedcff2020-11-25 02:16:32 -0800173logged, but that the regular attribute *name* is not logged:
174
175.. testcode::
176 :hide:
177
178 import logging, sys
179 logging.basicConfig(level=logging.INFO, stream=sys.stdout, force=True)
180
181.. doctest::
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700182
183 >>> mary = Person('Mary M', 30) # The initial age update is logged
184 INFO:root:Updating 'age' to 30
185 >>> dave = Person('David D', 40)
186 INFO:root:Updating 'age' to 40
187
188 >>> vars(mary) # The actual data is in a private attribute
189 {'name': 'Mary M', '_age': 30}
190 >>> vars(dave)
191 {'name': 'David D', '_age': 40}
192
193 >>> mary.age # Access the data and log the lookup
194 INFO:root:Accessing 'age' giving 30
195 30
196 >>> mary.birthday() # Updates are logged as well
197 INFO:root:Accessing 'age' giving 30
198 INFO:root:Updating 'age' to 31
199
200 >>> dave.name # Regular attribute lookup isn't logged
201 'David D'
202 >>> dave.age # Only the managed attribute is logged
203 INFO:root:Accessing 'age' giving 40
204 40
205
Raymond Hettinger4c239a32020-11-06 16:46:10 -0800206One major issue with this example is that the private name *_age* is hardwired in
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700207the *LoggedAgeAccess* class. That means that each instance can only have one
208logged attribute and that its name is unchangeable. In the next example,
209we'll fix that problem.
210
211
Miss Skeleton (bot)27c72ba2020-11-01 20:55:57 -0800212Customized names
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700213----------------
214
Raymond Hettinger4c239a32020-11-06 16:46:10 -0800215When a class uses descriptors, it can inform each descriptor about which
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700216variable name was used.
217
218In this example, the :class:`Person` class has two descriptor instances,
219*name* and *age*. When the :class:`Person` class is defined, it makes a
220callback to :meth:`__set_name__` in *LoggedAccess* so that the field names can
Miss Islington (bot)543724b2020-11-24 22:47:17 -0800221be recorded, giving each descriptor its own *public_name* and *private_name*:
222
223.. testcode::
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700224
225 import logging
226
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -0700227 logging.basicConfig(level=logging.INFO)
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700228
229 class LoggedAccess:
230
231 def __set_name__(self, owner, name):
232 self.public_name = name
Miss Islington (bot)ec0788c2020-11-15 18:27:56 -0800233 self.private_name = '_' + name
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700234
235 def __get__(self, obj, objtype=None):
236 value = getattr(obj, self.private_name)
237 logging.info('Accessing %r giving %r', self.public_name, value)
238 return value
239
240 def __set__(self, obj, value):
241 logging.info('Updating %r to %r', self.public_name, value)
242 setattr(obj, self.private_name, value)
243
244 class Person:
245
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800246 name = LoggedAccess() # First descriptor instance
247 age = LoggedAccess() # Second descriptor instance
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700248
249 def __init__(self, name, age):
250 self.name = name # Calls the first descriptor
251 self.age = age # Calls the second descriptor
252
253 def birthday(self):
254 self.age += 1
255
256An interactive session shows that the :class:`Person` class has called
257:meth:`__set_name__` so that the field names would be recorded. Here
Miss Islington (bot)543724b2020-11-24 22:47:17 -0800258we call :func:`vars` to look up the descriptor without triggering it:
259
260.. doctest::
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700261
262 >>> vars(vars(Person)['name'])
263 {'public_name': 'name', 'private_name': '_name'}
264 >>> vars(vars(Person)['age'])
265 {'public_name': 'age', 'private_name': '_age'}
266
Miss Islington (bot)0aedcff2020-11-25 02:16:32 -0800267The new class now logs access to both *name* and *age*:
268
269.. testcode::
270 :hide:
271
272 import logging, sys
273 logging.basicConfig(level=logging.INFO, stream=sys.stdout, force=True)
274
275.. doctest::
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700276
277 >>> pete = Person('Peter P', 10)
278 INFO:root:Updating 'name' to 'Peter P'
279 INFO:root:Updating 'age' to 10
280 >>> kate = Person('Catherine C', 20)
281 INFO:root:Updating 'name' to 'Catherine C'
282 INFO:root:Updating 'age' to 20
283
284The two *Person* instances contain only the private names::
285
286 >>> vars(pete)
287 {'_name': 'Peter P', '_age': 10}
288 >>> vars(kate)
289 {'_name': 'Catherine C', '_age': 20}
290
291
292Closing thoughts
293----------------
294
295A :term:`descriptor` is what we call any object that defines :meth:`__get__`,
296:meth:`__set__`, or :meth:`__delete__`.
297
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700298Optionally, descriptors can have a :meth:`__set_name__` method. This is only
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -0700299used in cases where a descriptor needs to know either the class where it was
Miss Islington (bot)ec0788c2020-11-15 18:27:56 -0800300created or the name of class variable it was assigned to. (This method, if
301present, is called even if the class is not a descriptor.)
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700302
Miss Islington (bot)ec0788c2020-11-15 18:27:56 -0800303Descriptors get invoked by the dot "operator" during attribute lookup. If a
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700304descriptor is accessed indirectly with ``vars(some_class)[descriptor_name]``,
305the descriptor instance is returned without invoking it.
306
307Descriptors only work when used as class variables. When put in instances,
308they have no effect.
309
310The main motivation for descriptors is to provide a hook allowing objects
Miss Islington (bot)ec0788c2020-11-15 18:27:56 -0800311stored in class variables to control what happens during attribute lookup.
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700312
313Traditionally, the calling class controls what happens during lookup.
314Descriptors invert that relationship and allow the data being looked-up to
315have a say in the matter.
316
317Descriptors are used throughout the language. It is how functions turn into
318bound methods. Common tools like :func:`classmethod`, :func:`staticmethod`,
319:func:`property`, and :func:`functools.cached_property` are all implemented as
320descriptors.
321
322
323Complete Practical Example
324^^^^^^^^^^^^^^^^^^^^^^^^^^
325
326In this example, we create a practical and powerful tool for locating
327notoriously hard to find data corruption bugs.
328
329
330Validator class
331---------------
332
333A validator is a descriptor for managed attribute access. Prior to storing
334any data, it verifies that the new value meets various type and range
335restrictions. If those restrictions aren't met, it raises an exception to
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700336prevent data corruption at its source.
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700337
338This :class:`Validator` class is both an :term:`abstract base class` and a
Miss Islington (bot)543724b2020-11-24 22:47:17 -0800339managed attribute descriptor:
340
341.. testcode::
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700342
343 from abc import ABC, abstractmethod
344
345 class Validator(ABC):
346
347 def __set_name__(self, owner, name):
Miss Islington (bot)ec0788c2020-11-15 18:27:56 -0800348 self.private_name = '_' + name
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700349
350 def __get__(self, obj, objtype=None):
351 return getattr(obj, self.private_name)
352
353 def __set__(self, obj, value):
354 self.validate(value)
355 setattr(obj, self.private_name, value)
356
357 @abstractmethod
358 def validate(self, value):
359 pass
360
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -0700361Custom validators need to inherit from :class:`Validator` and must supply a
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700362:meth:`validate` method to test various restrictions as needed.
363
364
365Custom validators
366-----------------
367
368Here are three practical data validation utilities:
369
3701) :class:`OneOf` verifies that a value is one of a restricted set of options.
371
3722) :class:`Number` verifies that a value is either an :class:`int` or
373 :class:`float`. Optionally, it verifies that a value is between a given
374 minimum or maximum.
375
3763) :class:`String` verifies that a value is a :class:`str`. Optionally, it
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -0700377 validates a given minimum or maximum length. It can validate a
378 user-defined `predicate
379 <https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)>`_ as well.
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700380
Miss Islington (bot)543724b2020-11-24 22:47:17 -0800381.. testcode::
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700382
383 class OneOf(Validator):
384
385 def __init__(self, *options):
386 self.options = set(options)
387
388 def validate(self, value):
389 if value not in self.options:
390 raise ValueError(f'Expected {value!r} to be one of {self.options!r}')
391
392 class Number(Validator):
393
394 def __init__(self, minvalue=None, maxvalue=None):
395 self.minvalue = minvalue
396 self.maxvalue = maxvalue
397
398 def validate(self, value):
399 if not isinstance(value, (int, float)):
400 raise TypeError(f'Expected {value!r} to be an int or float')
401 if self.minvalue is not None and value < self.minvalue:
402 raise ValueError(
403 f'Expected {value!r} to be at least {self.minvalue!r}'
404 )
405 if self.maxvalue is not None and value > self.maxvalue:
406 raise ValueError(
407 f'Expected {value!r} to be no more than {self.maxvalue!r}'
408 )
409
410 class String(Validator):
411
412 def __init__(self, minsize=None, maxsize=None, predicate=None):
413 self.minsize = minsize
414 self.maxsize = maxsize
415 self.predicate = predicate
416
417 def validate(self, value):
418 if not isinstance(value, str):
419 raise TypeError(f'Expected {value!r} to be an str')
420 if self.minsize is not None and len(value) < self.minsize:
421 raise ValueError(
422 f'Expected {value!r} to be no smaller than {self.minsize!r}'
423 )
424 if self.maxsize is not None and len(value) > self.maxsize:
425 raise ValueError(
426 f'Expected {value!r} to be no bigger than {self.maxsize!r}'
427 )
428 if self.predicate is not None and not self.predicate(value):
429 raise ValueError(
430 f'Expected {self.predicate} to be true for {value!r}'
431 )
432
433
Miss Islington (bot)543724b2020-11-24 22:47:17 -0800434Practical application
435---------------------
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700436
Miss Islington (bot)543724b2020-11-24 22:47:17 -0800437Here's how the data validators can be used in a real class:
438
439.. testcode::
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700440
441 class Component:
442
443 name = String(minsize=3, maxsize=10, predicate=str.isupper)
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -0700444 kind = OneOf('wood', 'metal', 'plastic')
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700445 quantity = Number(minvalue=0)
446
447 def __init__(self, name, kind, quantity):
448 self.name = name
449 self.kind = kind
450 self.quantity = quantity
451
Miss Islington (bot)0aedcff2020-11-25 02:16:32 -0800452The descriptors prevent invalid instances from being created:
453
454.. doctest::
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700455
Miss Islington (bot)543724b2020-11-24 22:47:17 -0800456 >>> Component('Widget', 'metal', 5) # Blocked: 'Widget' is not all uppercase
457 Traceback (most recent call last):
458 ...
459 ValueError: Expected <method 'isupper' of 'str' objects> to be true for 'Widget'
460
461 >>> Component('WIDGET', 'metle', 5) # Blocked: 'metle' is misspelled
462 Traceback (most recent call last):
463 ...
464 ValueError: Expected 'metle' to be one of {'metal', 'plastic', 'wood'}
465
466 >>> Component('WIDGET', 'metal', -5) # Blocked: -5 is negative
467 Traceback (most recent call last):
468 ...
469 ValueError: Expected -5 to be at least 0
470 >>> Component('WIDGET', 'metal', 'V') # Blocked: 'V' isn't a number
471 Traceback (most recent call last):
472 ...
473 TypeError: Expected 'V' to be an int or float
474
475 >>> c = Component('WIDGET', 'metal', 5) # Allowed: The inputs are valid
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700476
477
478Technical Tutorial
479^^^^^^^^^^^^^^^^^^
480
481What follows is a more technical tutorial for the mechanics and details of how
482descriptors work.
483
484
Georg Brandl45cceeb2010-05-19 21:39:51 +0000485Abstract
486--------
487
488Defines descriptors, summarizes the protocol, and shows how descriptors are
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -0700489called. Provides an example showing how object relational mappings work.
Georg Brandl45cceeb2010-05-19 21:39:51 +0000490
491Learning about descriptors not only provides access to a larger toolset, it
Miss Islington (bot)ec0788c2020-11-15 18:27:56 -0800492creates a deeper understanding of how Python works.
Georg Brandl45cceeb2010-05-19 21:39:51 +0000493
494
Miss Skeleton (bot)27c72ba2020-11-01 20:55:57 -0800495Definition and introduction
Georg Brandl45cceeb2010-05-19 21:39:51 +0000496---------------------------
497
Miss Islington (bot)ec0788c2020-11-15 18:27:56 -0800498In general, a descriptor is an attribute value that has one of the methods in
499the descriptor protocol. Those methods are :meth:`__get__`, :meth:`__set__`,
500and :meth:`__delete__`. If any of those methods are defined for an the
501attribute, it is said to be a :term:`descriptor`.
Georg Brandl45cceeb2010-05-19 21:39:51 +0000502
503The default behavior for attribute access is to get, set, or delete the
504attribute from an object's dictionary. For instance, ``a.x`` has a lookup chain
505starting with ``a.__dict__['x']``, then ``type(a).__dict__['x']``, and
Miss Islington (bot)ec0788c2020-11-15 18:27:56 -0800506continuing through the method resolution order of ``type(a)``. If the
Georg Brandl45cceeb2010-05-19 21:39:51 +0000507looked-up value is an object defining one of the descriptor methods, then Python
508may override the default behavior and invoke the descriptor method instead.
509Where this occurs in the precedence chain depends on which descriptor methods
Florent Xiclunaaa6c1d22011-12-12 18:54:29 +0100510were defined.
Georg Brandl45cceeb2010-05-19 21:39:51 +0000511
512Descriptors are a powerful, general purpose protocol. They are the mechanism
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700513behind properties, methods, static methods, class methods, and
514:func:`super()`. They are used throughout Python itself. Descriptors
515simplify the underlying C code and offer a flexible set of new tools for
516everyday Python programs.
Georg Brandl45cceeb2010-05-19 21:39:51 +0000517
518
Miss Skeleton (bot)27c72ba2020-11-01 20:55:57 -0800519Descriptor protocol
Georg Brandl45cceeb2010-05-19 21:39:51 +0000520-------------------
521
NotAFile28ea4c22018-09-10 23:35:38 +0200522``descr.__get__(self, obj, type=None) -> value``
Georg Brandl45cceeb2010-05-19 21:39:51 +0000523
NotAFile28ea4c22018-09-10 23:35:38 +0200524``descr.__set__(self, obj, value) -> None``
Georg Brandl45cceeb2010-05-19 21:39:51 +0000525
NotAFile28ea4c22018-09-10 23:35:38 +0200526``descr.__delete__(self, obj) -> None``
Georg Brandl45cceeb2010-05-19 21:39:51 +0000527
528That is all there is to it. Define any of these methods and an object is
529considered a descriptor and can override default behavior upon being looked up
530as an attribute.
531
Aaron Hall, MBA4054b172018-05-20 19:46:42 -0400532If an object defines :meth:`__set__` or :meth:`__delete__`, it is considered
Georg Brandl45cceeb2010-05-19 21:39:51 +0000533a data descriptor. Descriptors that only define :meth:`__get__` are called
Miss Islington (bot)ec0788c2020-11-15 18:27:56 -0800534non-data descriptors (they are often used for methods but other uses are
Georg Brandl45cceeb2010-05-19 21:39:51 +0000535possible).
536
537Data and non-data descriptors differ in how overrides are calculated with
538respect to entries in an instance's dictionary. If an instance's dictionary
539has an entry with the same name as a data descriptor, the data descriptor
540takes precedence. If an instance's dictionary has an entry with the same
541name as a non-data descriptor, the dictionary entry takes precedence.
542
543To make a read-only data descriptor, define both :meth:`__get__` and
544:meth:`__set__` with the :meth:`__set__` raising an :exc:`AttributeError` when
545called. Defining the :meth:`__set__` method with an exception raising
546placeholder is enough to make it a data descriptor.
547
548
Miss Skeleton (bot)27c72ba2020-11-01 20:55:57 -0800549Overview of descriptor invocation
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800550---------------------------------
Georg Brandl45cceeb2010-05-19 21:39:51 +0000551
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800552A descriptor can be called directly with ``desc.__get__(obj)`` or
553``desc.__get__(None, cls)``.
Georg Brandl45cceeb2010-05-19 21:39:51 +0000554
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700555But it is more common for a descriptor to be invoked automatically from
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800556attribute access.
557
558The expression ``obj.x`` looks up the attribute ``x`` in the chain of
Miss Islington (bot)ec0788c2020-11-15 18:27:56 -0800559namespaces for ``obj``. If the search finds a descriptor outside of the
560instance ``__dict__``, its :meth:`__get__` method is invoked according to the
561precedence rules listed below.
Georg Brandl45cceeb2010-05-19 21:39:51 +0000562
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700563The details of invocation depend on whether ``obj`` is an object, class, or
564instance of super.
Georg Brandl45cceeb2010-05-19 21:39:51 +0000565
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700566
Miss Skeleton (bot)27c72ba2020-11-01 20:55:57 -0800567Invocation from an instance
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800568---------------------------
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700569
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800570Instance lookup scans through a chain of namespaces giving data descriptors
571the highest priority, followed by instance variables, then non-data
572descriptors, then class variables, and lastly :meth:`__getattr__` if it is
573provided.
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700574
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800575If a descriptor is found for ``a.x``, then it is invoked with:
576``desc.__get__(a, type(a))``.
Georg Brandl45cceeb2010-05-19 21:39:51 +0000577
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800578The logic for a dotted lookup is in :meth:`object.__getattribute__`. Here is
Miss Islington (bot)543724b2020-11-24 22:47:17 -0800579a pure Python equivalent:
580
581.. testcode::
Georg Brandl45cceeb2010-05-19 21:39:51 +0000582
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800583 def object_getattribute(obj, name):
584 "Emulate PyObject_GenericGetAttr() in Objects/object.c"
585 null = object()
586 objtype = type(obj)
Miss Islington (bot)ec0788c2020-11-15 18:27:56 -0800587 cls_var = getattr(objtype, name, null)
588 descr_get = getattr(type(cls_var), '__get__', null)
589 if descr_get is not null:
590 if (hasattr(type(cls_var), '__set__')
591 or hasattr(type(cls_var), '__delete__')):
592 return descr_get(cls_var, obj, objtype) # data descriptor
593 if hasattr(obj, '__dict__') and name in vars(obj):
594 return vars(obj)[name] # instance variable
595 if descr_get is not null:
596 return descr_get(cls_var, obj, objtype) # non-data descriptor
597 if cls_var is not null:
598 return cls_var # class variable
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800599 raise AttributeError(name)
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700600
Miss Islington (bot)543724b2020-11-24 22:47:17 -0800601
602.. testcode::
603 :hide:
604
605 # Test the fidelity of object_getattribute() by comparing it with the
606 # normal object.__getattribute__(). The former will be accessed by
607 # square brackets and the latter by the dot operator.
608
609 class Object:
610
611 def __getitem__(obj, name):
612 try:
613 return object_getattribute(obj, name)
614 except AttributeError:
615 if not hasattr(type(obj), '__getattr__'):
616 raise
617 return type(obj).__getattr__(obj, name) # __getattr__
618
619 class DualOperator(Object):
620
621 x = 10
622
623 def __init__(self, z):
624 self.z = z
625
626 @property
627 def p2(self):
628 return 2 * self.x
629
630 @property
631 def p3(self):
632 return 3 * self.x
633
634 def m5(self, y):
635 return 5 * y
636
637 def m7(self, y):
638 return 7 * y
639
640 def __getattr__(self, name):
641 return ('getattr_hook', self, name)
642
643 class DualOperatorWithSlots:
644
645 __getitem__ = Object.__getitem__
646
647 __slots__ = ['z']
648
649 x = 15
650
651 def __init__(self, z):
652 self.z = z
653
654 @property
655 def p2(self):
656 return 2 * self.x
657
658 def m5(self, y):
659 return 5 * y
660
661 def __getattr__(self, name):
662 return ('getattr_hook', self, name)
663
664
665.. doctest::
666 :hide:
667
668 >>> a = DualOperator(11)
669 >>> vars(a).update(p3 = '_p3', m7 = '_m7')
670 >>> a.x == a['x'] == 10
671 True
672 >>> a.z == a['z'] == 11
673 True
674 >>> a.p2 == a['p2'] == 20
675 True
676 >>> a.p3 == a['p3'] == 30
677 True
678 >>> a.m5(100) == a.m5(100) == 500
679 True
680 >>> a.m7 == a['m7'] == '_m7'
681 True
682 >>> a.g == a['g'] == ('getattr_hook', a, 'g')
683 True
684
685 >>> b = DualOperatorWithSlots(22)
686 >>> b.x == b['x'] == 15
687 True
688 >>> b.z == b['z'] == 22
689 True
690 >>> b.p2 == b['p2'] == 30
691 True
692 >>> b.m5(200) == b['m5'](200) == 1000
693 True
694 >>> b.g == b['g'] == ('getattr_hook', b, 'g')
695 True
696
697
Miss Islington (bot)ec0788c2020-11-15 18:27:56 -0800698Interestingly, attribute lookup doesn't call :meth:`object.__getattribute__`
699directly. Instead, both the dot operator and the :func:`getattr` function
Miss Islington (bot)543724b2020-11-24 22:47:17 -0800700perform attribute lookup by way of a helper function:
701
702.. testcode::
Miss Islington (bot)ec0788c2020-11-15 18:27:56 -0800703
704 def getattr_hook(obj, name):
705 "Emulate slot_tp_getattr_hook() in Objects/typeobject.c"
706 try:
707 return obj.__getattribute__(name)
708 except AttributeError:
709 if not hasattr(type(obj), '__getattr__'):
710 raise
711 return type(obj).__getattr__(obj, name) # __getattr__
712
713So if :meth:`__getattr__` exists, it is called whenever :meth:`__getattribute__`
714raises :exc:`AttributeError` (either directly or in one of the descriptor calls).
715
716Also, if a user calls :meth:`object.__getattribute__` directly, the
717:meth:`__getattr__` hook is bypassed entirely.
Georg Brandl45cceeb2010-05-19 21:39:51 +0000718
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800719
Miss Skeleton (bot)27c72ba2020-11-01 20:55:57 -0800720Invocation from a class
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800721-----------------------
722
723The logic for a dotted lookup such as ``A.x`` is in
724:meth:`type.__getattribute__`. The steps are similar to those for
725:meth:`object.__getattribute__` but the instance dictionary lookup is replaced
726by a search through the class's :term:`method resolution order`.
727
728If a descriptor is found, it is invoked with ``desc.__get__(None, A)``.
729
730The full C implementation can be found in :c:func:`type_getattro()` and
731:c:func:`_PyType_Lookup()` in :source:`Objects/typeobject.c`.
732
733
Miss Skeleton (bot)27c72ba2020-11-01 20:55:57 -0800734Invocation from super
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800735---------------------
736
737The logic for super's dotted lookup is in the :meth:`__getattribute__` method for
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700738object returned by :class:`super()`.
Georg Brandl45cceeb2010-05-19 21:39:51 +0000739
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800740A dotted lookup such as ``super(A, obj).m`` searches ``obj.__class__.__mro__``
741for the base class ``B`` immediately following ``A`` and then returns
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -0700742``B.__dict__['m'].__get__(obj, A)``. If not a descriptor, ``m`` is returned
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800743unchanged.
Georg Brandl45cceeb2010-05-19 21:39:51 +0000744
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800745The full C implementation can be found in :c:func:`super_getattro()` in
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700746:source:`Objects/typeobject.c`. A pure Python equivalent can be found in
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800747`Guido's Tutorial
748<https://www.python.org/download/releases/2.2.3/descrintro/#cooperation>`_.
Georg Brandl45cceeb2010-05-19 21:39:51 +0000749
Georg Brandl45cceeb2010-05-19 21:39:51 +0000750
Miss Skeleton (bot)27c72ba2020-11-01 20:55:57 -0800751Summary of invocation logic
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800752---------------------------
753
754The mechanism for descriptors is embedded in the :meth:`__getattribute__()`
755methods for :class:`object`, :class:`type`, and :func:`super`.
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700756
757The important points to remember are:
758
759* Descriptors are invoked by the :meth:`__getattribute__` method.
760
761* Classes inherit this machinery from :class:`object`, :class:`type`, or
762 :func:`super`.
763
764* Overriding :meth:`__getattribute__` prevents automatic descriptor calls
765 because all the descriptor logic is in that method.
766
767* :meth:`object.__getattribute__` and :meth:`type.__getattribute__` make
768 different calls to :meth:`__get__`. The first includes the instance and may
769 include the class. The second puts in ``None`` for the instance and always
770 includes the class.
771
772* Data descriptors always override instance dictionaries.
773
774* Non-data descriptors may be overridden by instance dictionaries.
Georg Brandl45cceeb2010-05-19 21:39:51 +0000775
776
Miss Skeleton (bot)27c72ba2020-11-01 20:55:57 -0800777Automatic name notification
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700778---------------------------
779
780Sometimes it is desirable for a descriptor to know what class variable name it
781was assigned to. When a new class is created, the :class:`type` metaclass
782scans the dictionary of the new class. If any of the entries are descriptors
783and if they define :meth:`__set_name__`, that method is called with two
Raymond Hettinger4c239a32020-11-06 16:46:10 -0800784arguments. The *owner* is the class where the descriptor is used, and the
785*name* is the class variable the descriptor was assigned to.
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700786
787The implementation details are in :c:func:`type_new()` and
788:c:func:`set_names()` in :source:`Objects/typeobject.c`.
789
790Since the update logic is in :meth:`type.__new__`, notifications only take
791place at the time of class creation. If descriptors are added to the class
792afterwards, :meth:`__set_name__` will need to be called manually.
793
794
Miss Skeleton (bot)27c72ba2020-11-01 20:55:57 -0800795ORM example
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -0700796-----------
Georg Brandl45cceeb2010-05-19 21:39:51 +0000797
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700798The following code is simplified skeleton showing how data descriptors could
799be used to implement an `object relational mapping
800<https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping>`_.
Georg Brandl45cceeb2010-05-19 21:39:51 +0000801
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -0700802The essential idea is that the data is stored in an external database. The
803Python instances only hold keys to the database's tables. Descriptors take
Miss Islington (bot)543724b2020-11-24 22:47:17 -0800804care of lookups or updates:
805
806.. testcode::
Georg Brandl45cceeb2010-05-19 21:39:51 +0000807
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700808 class Field:
809
810 def __set_name__(self, owner, name):
811 self.fetch = f'SELECT {name} FROM {owner.table} WHERE {owner.key}=?;'
812 self.store = f'UPDATE {owner.table} SET {name}=? WHERE {owner.key}=?;'
Georg Brandl45cceeb2010-05-19 21:39:51 +0000813
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700814 def __get__(self, obj, objtype=None):
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700815 return conn.execute(self.fetch, [obj.key]).fetchone()[0]
Georg Brandl45cceeb2010-05-19 21:39:51 +0000816
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700817 def __set__(self, obj, value):
818 conn.execute(self.store, [value, obj.key])
819 conn.commit()
Georg Brandl45cceeb2010-05-19 21:39:51 +0000820
Miss Islington (bot)543724b2020-11-24 22:47:17 -0800821We can use the :class:`Field` class to define `models
822<https://en.wikipedia.org/wiki/Database_model>`_ that describe the schema for
823each table in a database:
824
825.. testcode::
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -0700826
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700827 class Movie:
828 table = 'Movies' # Table name
829 key = 'title' # Primary key
830 director = Field()
831 year = Field()
Georg Brandl45cceeb2010-05-19 21:39:51 +0000832
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700833 def __init__(self, key):
834 self.key = key
835
836 class Song:
837 table = 'Music'
838 key = 'title'
839 artist = Field()
840 year = Field()
841 genre = Field()
842
843 def __init__(self, key):
844 self.key = key
845
Miss Islington (bot)543724b2020-11-24 22:47:17 -0800846To use the models, first connect to the database::
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700847
848 >>> import sqlite3
849 >>> conn = sqlite3.connect('entertainment.db')
850
Miss Islington (bot)543724b2020-11-24 22:47:17 -0800851An interactive session shows how data is retrieved from the database and how
852it can be updated:
853
854.. testsetup::
855
856 song_data = [
857 ('Country Roads', 'John Denver', 1972),
858 ('Me and Bobby McGee', 'Janice Joplin', 1971),
859 ('Coal Miners Daughter', 'Loretta Lynn', 1970),
860 ]
861
862 movie_data = [
863 ('Star Wars', 'George Lucas', 1977),
864 ('Jaws', 'Steven Spielberg', 1975),
865 ('Aliens', 'James Cameron', 1986),
866 ]
867
868 import sqlite3
869
870 conn = sqlite3.connect(':memory:')
871 conn.execute('CREATE TABLE Music (title text, artist text, year integer);')
872 conn.execute('CREATE INDEX MusicNdx ON Music (title);')
873 conn.executemany('INSERT INTO Music VALUES (?, ?, ?);', song_data)
874 conn.execute('CREATE TABLE Movies (title text, director text, year integer);')
875 conn.execute('CREATE INDEX MovieNdx ON Music (title);')
876 conn.executemany('INSERT INTO Movies VALUES (?, ?, ?);', movie_data)
877 conn.commit()
878
879.. doctest::
880
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700881 >>> Movie('Star Wars').director
882 'George Lucas'
883 >>> jaws = Movie('Jaws')
884 >>> f'Released in {jaws.year} by {jaws.director}'
885 'Released in 1975 by Steven Spielberg'
886
887 >>> Song('Country Roads').artist
888 'John Denver'
889
890 >>> Movie('Star Wars').director = 'J.J. Abrams'
891 >>> Movie('Star Wars').director
892 'J.J. Abrams'
893
Miss Islington (bot)ec0788c2020-11-15 18:27:56 -0800894
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -0700895Pure Python Equivalents
896^^^^^^^^^^^^^^^^^^^^^^^
897
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700898The descriptor protocol is simple and offers exciting possibilities. Several
Miss Skeleton (bot)81dd2c02020-11-01 10:09:46 -0800899use cases are so common that they have been prepackaged into built-in tools.
Miss Skeleton (bot)27c72ba2020-11-01 20:55:57 -0800900Properties, bound methods, static methods, class methods, and \_\_slots\_\_ are
901all based on the descriptor protocol.
Georg Brandl45cceeb2010-05-19 21:39:51 +0000902
903
904Properties
905----------
906
907Calling :func:`property` is a succinct way of building a data descriptor that
Raymond Hettinger4c239a32020-11-06 16:46:10 -0800908triggers a function call upon access to an attribute. Its signature is::
Georg Brandl45cceeb2010-05-19 21:39:51 +0000909
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -0700910 property(fget=None, fset=None, fdel=None, doc=None) -> property
Georg Brandl45cceeb2010-05-19 21:39:51 +0000911
Miss Islington (bot)543724b2020-11-24 22:47:17 -0800912The documentation shows a typical use to define a managed attribute ``x``:
913
914.. testcode::
Georg Brandl45cceeb2010-05-19 21:39:51 +0000915
Serhiy Storchakae042a452019-06-10 13:35:52 +0300916 class C:
Georg Brandl45cceeb2010-05-19 21:39:51 +0000917 def getx(self): return self.__x
918 def setx(self, value): self.__x = value
919 def delx(self): del self.__x
920 x = property(getx, setx, delx, "I'm the 'x' property.")
921
922To see how :func:`property` is implemented in terms of the descriptor protocol,
Miss Islington (bot)543724b2020-11-24 22:47:17 -0800923here is a pure Python equivalent:
924
925.. testcode::
Georg Brandl45cceeb2010-05-19 21:39:51 +0000926
Serhiy Storchakae042a452019-06-10 13:35:52 +0300927 class Property:
Georg Brandl45cceeb2010-05-19 21:39:51 +0000928 "Emulate PyProperty_Type() in Objects/descrobject.c"
929
930 def __init__(self, fget=None, fset=None, fdel=None, doc=None):
931 self.fget = fget
932 self.fset = fset
933 self.fdel = fdel
Raymond Hettinger632c8c82013-03-10 09:41:18 -0700934 if doc is None and fget is not None:
935 doc = fget.__doc__
Georg Brandl45cceeb2010-05-19 21:39:51 +0000936 self.__doc__ = doc
937
938 def __get__(self, obj, objtype=None):
939 if obj is None:
940 return self
941 if self.fget is None:
Raymond Hettinger632c8c82013-03-10 09:41:18 -0700942 raise AttributeError("unreadable attribute")
Georg Brandl45cceeb2010-05-19 21:39:51 +0000943 return self.fget(obj)
944
945 def __set__(self, obj, value):
946 if self.fset is None:
Raymond Hettinger632c8c82013-03-10 09:41:18 -0700947 raise AttributeError("can't set attribute")
Georg Brandl45cceeb2010-05-19 21:39:51 +0000948 self.fset(obj, value)
949
950 def __delete__(self, obj):
951 if self.fdel is None:
Raymond Hettinger632c8c82013-03-10 09:41:18 -0700952 raise AttributeError("can't delete attribute")
Georg Brandl45cceeb2010-05-19 21:39:51 +0000953 self.fdel(obj)
954
Raymond Hettinger632c8c82013-03-10 09:41:18 -0700955 def getter(self, fget):
956 return type(self)(fget, self.fset, self.fdel, self.__doc__)
957
958 def setter(self, fset):
959 return type(self)(self.fget, fset, self.fdel, self.__doc__)
960
961 def deleter(self, fdel):
962 return type(self)(self.fget, self.fset, fdel, self.__doc__)
963
Miss Islington (bot)543724b2020-11-24 22:47:17 -0800964.. testcode::
965 :hide:
966
967 # Verify the Property() emulation
968
969 class CC:
970 def getx(self):
971 return self.__x
972 def setx(self, value):
973 self.__x = value
974 def delx(self):
975 del self.__x
976 x = Property(getx, setx, delx, "I'm the 'x' property.")
977
978 # Now do it again but use the decorator style
979
980 class CCC:
981 @Property
982 def x(self):
983 return self.__x
984 @x.setter
985 def x(self, value):
986 self.__x = value
987 @x.deleter
988 def x(self):
989 del self.__x
990
991
992.. doctest::
993 :hide:
994
995 >>> cc = CC()
996 >>> hasattr(cc, 'x')
997 False
998 >>> cc.x = 33
999 >>> cc.x
1000 33
1001 >>> del cc.x
1002 >>> hasattr(cc, 'x')
1003 False
1004
1005 >>> ccc = CCC()
1006 >>> hasattr(ccc, 'x')
1007 False
1008 >>> ccc.x = 333
1009 >>> ccc.x == 333
1010 True
1011 >>> del ccc.x
1012 >>> hasattr(ccc, 'x')
1013 False
1014
Georg Brandl45cceeb2010-05-19 21:39:51 +00001015The :func:`property` builtin helps whenever a user interface has granted
1016attribute access and then subsequent changes require the intervention of a
1017method.
1018
1019For instance, a spreadsheet class may grant access to a cell value through
1020``Cell('b10').value``. Subsequent improvements to the program require the cell
1021to be recalculated on every access; however, the programmer does not want to
1022affect existing client code accessing the attribute directly. The solution is
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001023to wrap access to the value attribute in a property data descriptor:
1024
1025.. testcode::
Georg Brandl45cceeb2010-05-19 21:39:51 +00001026
Serhiy Storchakae042a452019-06-10 13:35:52 +03001027 class Cell:
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -07001028 ...
1029
1030 @property
1031 def value(self):
_ = NaNb066edf2017-06-23 11:54:35 +08001032 "Recalculate the cell before returning value"
Georg Brandl45cceeb2010-05-19 21:39:51 +00001033 self.recalc()
_ = NaNb066edf2017-06-23 11:54:35 +08001034 return self._value
Georg Brandl45cceeb2010-05-19 21:39:51 +00001035
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001036Either the built-in :func:`property` or our :func:`Property` equivalent would
1037work in this example.
1038
Georg Brandl45cceeb2010-05-19 21:39:51 +00001039
Miss Skeleton (bot)27c72ba2020-11-01 20:55:57 -08001040Functions and methods
Georg Brandl45cceeb2010-05-19 21:39:51 +00001041---------------------
1042
1043Python's object oriented features are built upon a function based environment.
1044Using non-data descriptors, the two are merged seamlessly.
1045
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -07001046Functions stored in class dictionaries get turned into methods when invoked.
1047Methods only differ from regular functions in that the object instance is
1048prepended to the other arguments. By convention, the instance is called
1049*self* but could be called *this* or any other variable name.
Georg Brandl45cceeb2010-05-19 21:39:51 +00001050
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -07001051Methods can be created manually with :class:`types.MethodType` which is
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001052roughly equivalent to:
1053
1054.. testcode::
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -07001055
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -07001056 class MethodType:
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -07001057 "Emulate Py_MethodType in Objects/classobject.c"
1058
1059 def __init__(self, func, obj):
1060 self.__func__ = func
1061 self.__self__ = obj
1062
1063 def __call__(self, *args, **kwargs):
1064 func = self.__func__
1065 obj = self.__self__
1066 return func(obj, *args, **kwargs)
1067
1068To support automatic creation of methods, functions include the
1069:meth:`__get__` method for binding methods during attribute access. This
Raymond Hettinger4c239a32020-11-06 16:46:10 -08001070means that functions are non-data descriptors that return bound methods
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001071during dotted lookup from an instance. Here's how it works:
1072
1073.. testcode::
Georg Brandl45cceeb2010-05-19 21:39:51 +00001074
Serhiy Storchakae042a452019-06-10 13:35:52 +03001075 class Function:
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -07001076 ...
1077
Georg Brandl45cceeb2010-05-19 21:39:51 +00001078 def __get__(self, obj, objtype=None):
1079 "Simulate func_descr_get() in Objects/funcobject.c"
Raymond Hettinger0d4497b2017-09-25 01:05:49 -07001080 if obj is None:
1081 return self
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -07001082 return MethodType(self, obj)
Georg Brandl45cceeb2010-05-19 21:39:51 +00001083
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -07001084Running the following class in the interpreter shows how the function
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001085descriptor works in practice:
1086
1087.. testcode::
Georg Brandl45cceeb2010-05-19 21:39:51 +00001088
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -07001089 class D:
1090 def f(self, x):
1091 return x
Raymond Hettinger0d4497b2017-09-25 01:05:49 -07001092
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001093The function has a :term:`qualified name` attribute to support introspection:
1094
1095.. doctest::
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -07001096
1097 >>> D.f.__qualname__
1098 'D.f'
1099
1100Accessing the function through the class dictionary does not invoke
1101:meth:`__get__`. Instead, it just returns the underlying function object::
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -07001102
Raymond Hettinger0d4497b2017-09-25 01:05:49 -07001103 >>> D.__dict__['f']
1104 <function D.f at 0x00C45070>
1105
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -07001106Dotted access from a class calls :meth:`__get__` which just returns the
1107underlying function unchanged::
1108
Raymond Hettinger0d4497b2017-09-25 01:05:49 -07001109 >>> D.f
1110 <function D.f at 0x00C45070>
1111
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -07001112The interesting behavior occurs during dotted access from an instance. The
1113dotted lookup calls :meth:`__get__` which returns a bound method object::
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -07001114
1115 >>> d = D()
Raymond Hettinger0d4497b2017-09-25 01:05:49 -07001116 >>> d.f
Georg Brandl45cceeb2010-05-19 21:39:51 +00001117 <bound method D.f of <__main__.D object at 0x00B18C90>>
1118
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -07001119Internally, the bound method stores the underlying function and the bound
1120instance::
1121
Raymond Hettinger0d4497b2017-09-25 01:05:49 -07001122 >>> d.f.__func__
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001123 <function D.f at 0x00C45070>
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -07001124
Raymond Hettinger0d4497b2017-09-25 01:05:49 -07001125 >>> d.f.__self__
1126 <__main__.D object at 0x1012e1f98>
Georg Brandl45cceeb2010-05-19 21:39:51 +00001127
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -07001128If you have ever wondered where *self* comes from in regular methods or where
1129*cls* comes from in class methods, this is it!
1130
Georg Brandl45cceeb2010-05-19 21:39:51 +00001131
Miss Skeleton (bot)27c72ba2020-11-01 20:55:57 -08001132Static methods
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -07001133--------------
Georg Brandl45cceeb2010-05-19 21:39:51 +00001134
1135Non-data descriptors provide a simple mechanism for variations on the usual
1136patterns of binding functions into methods.
1137
1138To recap, functions have a :meth:`__get__` method so that they can be converted
Serhiy Storchakad65c9492015-11-02 14:10:23 +02001139to a method when accessed as attributes. The non-data descriptor transforms an
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -07001140``obj.f(*args)`` call into ``f(obj, *args)``. Calling ``cls.f(*args)``
Georg Brandl45cceeb2010-05-19 21:39:51 +00001141becomes ``f(*args)``.
1142
1143This chart summarizes the binding and its two most useful variants:
1144
1145 +-----------------+----------------------+------------------+
1146 | Transformation | Called from an | Called from a |
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -07001147 | | object | class |
Georg Brandl45cceeb2010-05-19 21:39:51 +00001148 +=================+======================+==================+
1149 | function | f(obj, \*args) | f(\*args) |
1150 +-----------------+----------------------+------------------+
1151 | staticmethod | f(\*args) | f(\*args) |
1152 +-----------------+----------------------+------------------+
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -07001153 | classmethod | f(type(obj), \*args) | f(cls, \*args) |
Georg Brandl45cceeb2010-05-19 21:39:51 +00001154 +-----------------+----------------------+------------------+
1155
1156Static methods return the underlying function without changes. Calling either
1157``c.f`` or ``C.f`` is the equivalent of a direct lookup into
1158``object.__getattribute__(c, "f")`` or ``object.__getattribute__(C, "f")``. As a
1159result, the function becomes identically accessible from either an object or a
1160class.
1161
1162Good candidates for static methods are methods that do not reference the
1163``self`` variable.
1164
1165For instance, a statistics package may include a container class for
1166experimental data. The class provides normal methods for computing the average,
1167mean, median, and other descriptive statistics that depend on the data. However,
1168there may be useful functions which are conceptually related but do not depend
1169on the data. For instance, ``erf(x)`` is handy conversion routine that comes up
1170in statistical work but does not directly depend on a particular dataset.
1171It can be called either from an object or the class: ``s.erf(1.5) --> .9332`` or
1172``Sample.erf(1.5) --> .9332``.
1173
Miss Skeleton (bot)3d43f1d2020-10-24 20:39:15 -07001174Since static methods return the underlying function with no changes, the
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001175example calls are unexciting:
1176
1177.. testcode::
Georg Brandl45cceeb2010-05-19 21:39:51 +00001178
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -07001179 class E:
1180 @staticmethod
1181 def f(x):
1182 print(x)
1183
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001184.. doctest::
1185
Shubham Aggarwalabbdd1f2019-03-20 08:25:55 +05301186 >>> E.f(3)
Georg Brandl45cceeb2010-05-19 21:39:51 +00001187 3
Shubham Aggarwalabbdd1f2019-03-20 08:25:55 +05301188 >>> E().f(3)
Georg Brandl45cceeb2010-05-19 21:39:51 +00001189 3
1190
1191Using the non-data descriptor protocol, a pure Python version of
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001192:func:`staticmethod` would look like this:
1193
1194.. doctest::
Georg Brandl45cceeb2010-05-19 21:39:51 +00001195
Serhiy Storchakae042a452019-06-10 13:35:52 +03001196 class StaticMethod:
Serhiy Storchakadba90392016-05-10 12:01:23 +03001197 "Emulate PyStaticMethod_Type() in Objects/funcobject.c"
Georg Brandl45cceeb2010-05-19 21:39:51 +00001198
Serhiy Storchakadba90392016-05-10 12:01:23 +03001199 def __init__(self, f):
1200 self.f = f
Georg Brandl45cceeb2010-05-19 21:39:51 +00001201
Serhiy Storchakadba90392016-05-10 12:01:23 +03001202 def __get__(self, obj, objtype=None):
1203 return self.f
Georg Brandl45cceeb2010-05-19 21:39:51 +00001204
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -07001205
Miss Skeleton (bot)27c72ba2020-11-01 20:55:57 -08001206Class methods
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -07001207-------------
1208
Georg Brandl45cceeb2010-05-19 21:39:51 +00001209Unlike static methods, class methods prepend the class reference to the
1210argument list before calling the function. This format is the same
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001211for whether the caller is an object or a class:
1212
1213.. testcode::
Georg Brandl45cceeb2010-05-19 21:39:51 +00001214
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -07001215 class F:
1216 @classmethod
1217 def f(cls, x):
1218 return cls.__name__, x
1219
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001220.. doctest::
1221
1222 >>> F.f(3)
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -07001223 ('F', 3)
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001224 >>> F().f(3)
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -07001225 ('F', 3)
Georg Brandl45cceeb2010-05-19 21:39:51 +00001226
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -07001227This behavior is useful whenever the method only needs to have a class
Miss Islington (bot)dca61e72020-11-25 12:14:32 -08001228reference and does not rely on data stored in a specific instance. One use for
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -07001229class methods is to create alternate class constructors. For example, the
1230classmethod :func:`dict.fromkeys` creates a new dictionary from a list of
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001231keys. The pure Python equivalent is:
Georg Brandl45cceeb2010-05-19 21:39:51 +00001232
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001233.. testcode::
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -07001234
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001235 class Dict(dict):
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -07001236 @classmethod
1237 def fromkeys(cls, iterable, value=None):
Georg Brandl45cceeb2010-05-19 21:39:51 +00001238 "Emulate dict_fromkeys() in Objects/dictobject.c"
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -07001239 d = cls()
Georg Brandl45cceeb2010-05-19 21:39:51 +00001240 for key in iterable:
1241 d[key] = value
1242 return d
Georg Brandl45cceeb2010-05-19 21:39:51 +00001243
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001244Now a new dictionary of unique keys can be constructed like this:
1245
1246.. doctest::
Georg Brandl45cceeb2010-05-19 21:39:51 +00001247
Miss Islington (bot)0aedcff2020-11-25 02:16:32 -08001248 >>> d = Dict.fromkeys('abracadabra')
1249 >>> type(d) is Dict
1250 True
1251 >>> d
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001252 {'a': None, 'b': None, 'r': None, 'c': None, 'd': None}
Georg Brandl45cceeb2010-05-19 21:39:51 +00001253
1254Using the non-data descriptor protocol, a pure Python version of
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001255:func:`classmethod` would look like this:
1256
1257.. testcode::
Georg Brandl45cceeb2010-05-19 21:39:51 +00001258
Serhiy Storchakae042a452019-06-10 13:35:52 +03001259 class ClassMethod:
Serhiy Storchakadba90392016-05-10 12:01:23 +03001260 "Emulate PyClassMethod_Type() in Objects/funcobject.c"
Georg Brandl45cceeb2010-05-19 21:39:51 +00001261
Serhiy Storchakadba90392016-05-10 12:01:23 +03001262 def __init__(self, f):
1263 self.f = f
Georg Brandl45cceeb2010-05-19 21:39:51 +00001264
Miss Skeleton (bot)f8d96b92020-10-23 13:49:32 -07001265 def __get__(self, obj, cls=None):
1266 if cls is None:
1267 cls = type(obj)
Miss Skeleton (bot)c17f63f2020-10-23 19:01:35 -07001268 if hasattr(obj, '__get__'):
1269 return self.f.__get__(cls)
Miss Skeleton (bot)2990aef2020-10-25 07:35:56 -07001270 return MethodType(self.f, cls)
Miss Skeleton (bot)c17f63f2020-10-23 19:01:35 -07001271
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001272.. testcode::
1273 :hide:
1274
1275 # Verify the emulation works
1276 class T:
1277 @ClassMethod
1278 def cm(cls, x, y):
1279 return (cls, x, y)
1280
1281.. doctest::
1282 :hide:
1283
1284 >>> T.cm(11, 22)
1285 (<class 'T'>, 11, 22)
1286
1287 # Also call it from an instance
1288 >>> t = T()
1289 >>> t.cm(11, 22)
1290 (<class 'T'>, 11, 22)
1291
Miss Skeleton (bot)c17f63f2020-10-23 19:01:35 -07001292The code path for ``hasattr(obj, '__get__')`` was added in Python 3.9 and
1293makes it possible for :func:`classmethod` to support chained decorators.
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001294For example, a classmethod and property could be chained together:
1295
1296.. testcode::
Miss Skeleton (bot)c17f63f2020-10-23 19:01:35 -07001297
1298 class G:
1299 @classmethod
1300 @property
1301 def __doc__(cls):
1302 return f'A doc for {cls.__name__!r}'
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001303
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001304.. doctest::
1305
1306 >>> G.__doc__
1307 "A doc for 'G'"
1308
1309
Miss Skeleton (bot)27c72ba2020-11-01 20:55:57 -08001310Member objects and __slots__
1311----------------------------
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001312
1313When a class defines ``__slots__``, it replaces instance dictionaries with a
1314fixed-length array of slot values. From a user point of view that has
1315several effects:
1316
13171. Provides immediate detection of bugs due to misspelled attribute
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001318assignments. Only attribute names specified in ``__slots__`` are allowed:
1319
1320.. testcode::
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001321
1322 class Vehicle:
1323 __slots__ = ('id_number', 'make', 'model')
1324
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001325.. doctest::
1326
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001327 >>> auto = Vehicle()
1328 >>> auto.id_nubmer = 'VYE483814LQEX'
1329 Traceback (most recent call last):
1330 ...
1331 AttributeError: 'Vehicle' object has no attribute 'id_nubmer'
1332
13332. Helps create immutable objects where descriptors manage access to private
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001334attributes stored in ``__slots__``:
1335
1336.. testcode::
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001337
1338 class Immutable:
1339
Raymond Hettinger4c239a32020-11-06 16:46:10 -08001340 __slots__ = ('_dept', '_name') # Replace the instance dictionary
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001341
1342 def __init__(self, dept, name):
1343 self._dept = dept # Store to private attribute
1344 self._name = name # Store to private attribute
1345
1346 @property # Read-only descriptor
1347 def dept(self):
1348 return self._dept
1349
1350 @property
1351 def name(self): # Read-only descriptor
1352 return self._name
1353
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001354.. doctest::
1355
1356 >>> mark = Immutable('Botany', 'Mark Watney')
1357 >>> mark.dept
1358 'Botany'
1359 >>> mark.dept = 'Space Pirate'
1360 Traceback (most recent call last):
1361 ...
1362 AttributeError: can't set attribute
1363 >>> mark.location = 'Mars'
1364 Traceback (most recent call last):
1365 ...
1366 AttributeError: 'Immutable' object has no attribute 'location'
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001367
13683. Saves memory. On a 64-bit Linux build, an instance with two attributes
1369takes 48 bytes with ``__slots__`` and 152 bytes without. This `flyweight
1370design pattern <https://en.wikipedia.org/wiki/Flyweight_pattern>`_ likely only
1371matters when a large number of instances are going to be created.
1372
13734. Blocks tools like :func:`functools.cached_property` which require an
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001374instance dictionary to function correctly:
1375
1376.. testcode::
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001377
1378 from functools import cached_property
1379
1380 class CP:
1381 __slots__ = () # Eliminates the instance dict
1382
1383 @cached_property # Requires an instance dict
1384 def pi(self):
1385 return 4 * sum((-1.0)**n / (2.0*n + 1.0)
1386 for n in reversed(range(100_000)))
1387
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001388.. doctest::
1389
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001390 >>> CP().pi
1391 Traceback (most recent call last):
1392 ...
1393 TypeError: No '__dict__' attribute on 'CP' instance to cache 'pi' property.
1394
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001395It is not possible to create an exact drop-in pure Python version of
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001396``__slots__`` because it requires direct access to C structures and control
1397over object memory allocation. However, we can build a mostly faithful
1398simulation where the actual C structure for slots is emulated by a private
1399``_slotvalues`` list. Reads and writes to that private structure are managed
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001400by member descriptors:
1401
1402.. testcode::
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001403
Miss Islington (bot)2019e4f2020-11-23 11:31:45 -08001404 null = object()
1405
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001406 class Member:
1407
1408 def __init__(self, name, clsname, offset):
1409 'Emulate PyMemberDef in Include/structmember.h'
1410 # Also see descr_new() in Objects/descrobject.c
1411 self.name = name
1412 self.clsname = clsname
1413 self.offset = offset
1414
1415 def __get__(self, obj, objtype=None):
1416 'Emulate member_get() in Objects/descrobject.c'
1417 # Also see PyMember_GetOne() in Python/structmember.c
Miss Islington (bot)2019e4f2020-11-23 11:31:45 -08001418 value = obj._slotvalues[self.offset]
1419 if value is null:
1420 raise AttributeError(self.name)
1421 return value
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001422
1423 def __set__(self, obj, value):
1424 'Emulate member_set() in Objects/descrobject.c'
1425 obj._slotvalues[self.offset] = value
1426
Miss Islington (bot)2019e4f2020-11-23 11:31:45 -08001427 def __delete__(self, obj):
1428 'Emulate member_delete() in Objects/descrobject.c'
1429 value = obj._slotvalues[self.offset]
1430 if value is null:
1431 raise AttributeError(self.name)
1432 obj._slotvalues[self.offset] = null
1433
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001434 def __repr__(self):
1435 'Emulate member_repr() in Objects/descrobject.c'
1436 return f'<Member {self.name!r} of {self.clsname!r}>'
1437
1438The :meth:`type.__new__` method takes care of adding member objects to class
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001439variables:
1440
1441.. testcode::
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001442
1443 class Type(type):
1444 'Simulate how the type metaclass adds member objects for slots'
1445
1446 def __new__(mcls, clsname, bases, mapping):
1447 'Emuluate type_new() in Objects/typeobject.c'
1448 # type_new() calls PyTypeReady() which calls add_methods()
1449 slot_names = mapping.get('slot_names', [])
1450 for offset, name in enumerate(slot_names):
1451 mapping[name] = Member(name, clsname, offset)
1452 return type.__new__(mcls, clsname, bases, mapping)
1453
Miss Islington (bot)2019e4f2020-11-23 11:31:45 -08001454The :meth:`object.__new__` method takes care of creating instances that have
1455slots instead of an instance dictionary. Here is a rough simulation in pure
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001456Python:
1457
1458.. testcode::
Miss Islington (bot)2019e4f2020-11-23 11:31:45 -08001459
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001460 class Object:
1461 'Simulate how object.__new__() allocates memory for __slots__'
1462
1463 def __new__(cls, *args):
1464 'Emulate object_new() in Objects/typeobject.c'
1465 inst = super().__new__(cls)
1466 if hasattr(cls, 'slot_names'):
Miss Islington (bot)2019e4f2020-11-23 11:31:45 -08001467 empty_slots = [null] * len(cls.slot_names)
1468 object.__setattr__(inst, '_slotvalues', empty_slots)
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001469 return inst
1470
Miss Islington (bot)2019e4f2020-11-23 11:31:45 -08001471 def __setattr__(self, name, value):
1472 'Emulate _PyObject_GenericSetAttrWithDict() Objects/object.c'
1473 cls = type(self)
1474 if hasattr(cls, 'slot_names') and name not in cls.slot_names:
1475 raise AttributeError(
1476 f'{type(self).__name__!r} object has no attribute {name!r}'
1477 )
1478 super().__setattr__(name, value)
1479
1480 def __delattr__(self, name):
1481 'Emulate _PyObject_GenericSetAttrWithDict() Objects/object.c'
1482 cls = type(self)
1483 if hasattr(cls, 'slot_names') and name not in cls.slot_names:
1484 raise AttributeError(
1485 f'{type(self).__name__!r} object has no attribute {name!r}'
1486 )
1487 super().__delattr__(name)
1488
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001489To use the simulation in a real class, just inherit from :class:`Object` and
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001490set the :term:`metaclass` to :class:`Type`:
1491
1492.. testcode::
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001493
1494 class H(Object, metaclass=Type):
Miss Islington (bot)2019e4f2020-11-23 11:31:45 -08001495 'Instance variables stored in slots'
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001496
1497 slot_names = ['x', 'y']
1498
1499 def __init__(self, x, y):
1500 self.x = x
1501 self.y = y
1502
1503At this point, the metaclass has loaded member objects for *x* and *y*::
1504
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001505 >>> from pprint import pp
1506 >>> pp(dict(vars(H)))
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001507 {'__module__': '__main__',
Miss Islington (bot)2019e4f2020-11-23 11:31:45 -08001508 '__doc__': 'Instance variables stored in slots',
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001509 'slot_names': ['x', 'y'],
1510 '__init__': <function H.__init__ at 0x7fb5d302f9d0>,
1511 'x': <Member 'x' of 'H'>,
Miss Islington (bot)2019e4f2020-11-23 11:31:45 -08001512 'y': <Member 'y' of 'H'>}
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001513
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001514.. doctest::
1515 :hide:
1516
1517 # We test this separately because the preceding section is not
1518 # doctestable due to the hex memory address for the __init__ function
1519 >>> isinstance(vars(H)['x'], Member)
1520 True
1521 >>> isinstance(vars(H)['y'], Member)
1522 True
1523
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001524When instances are created, they have a ``slot_values`` list where the
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001525attributes are stored:
1526
1527.. doctest::
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001528
1529 >>> h = H(10, 20)
1530 >>> vars(h)
1531 {'_slotvalues': [10, 20]}
1532 >>> h.x = 55
1533 >>> vars(h)
1534 {'_slotvalues': [55, 20]}
1535
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001536Misspelled or unassigned attributes will raise an exception:
1537
1538.. doctest::
Miss Skeleton (bot)d4cfbfe2020-11-01 18:27:58 -08001539
Miss Islington (bot)2019e4f2020-11-23 11:31:45 -08001540 >>> h.xz
1541 Traceback (most recent call last):
1542 ...
1543 AttributeError: 'H' object has no attribute 'xz'
Miss Islington (bot)543724b2020-11-24 22:47:17 -08001544
1545.. doctest::
1546 :hide:
1547
1548 # Examples for deleted attributes are not shown because this section
1549 # is already a bit lengthy. We still test that code here.
1550 >>> del h.x
1551 >>> hasattr(h, 'x')
1552 False
1553
1554 # Also test the code for uninitialized slots
1555 >>> class HU(Object, metaclass=Type):
1556 ... slot_names = ['x', 'y']
1557 ...
1558 >>> hu = HU()
1559 >>> hasattr(hu, 'x')
1560 False
1561 >>> hasattr(hu, 'y')
1562 False