1====================== 2Descriptor HowTo Guide 3====================== 4 5:Author: Raymond Hettinger 6:Contact: <python at rcn dot com> 7 8.. Contents:: 9 10Abstract 11-------- 12 13Defines descriptors, summarizes the protocol, and shows how descriptors are 14called. Examines a custom descriptor and several built-in Python descriptors 15including functions, properties, static methods, and class methods. Shows how 16each works by giving a pure Python equivalent and a sample application. 17 18Learning about descriptors not only provides access to a larger toolset, it 19creates a deeper understanding of how Python works and an appreciation for the 20elegance of its design. 21 22 23Definition and Introduction 24--------------------------- 25 26In general, a descriptor is an object attribute with "binding behavior", one 27whose attribute access has been overridden by methods in the descriptor 28protocol. Those methods are :meth:`__get__`, :meth:`__set__`, and 29:meth:`__delete__`. If any of those methods are defined for an object, it is 30said to be a descriptor. 31 32The default behavior for attribute access is to get, set, or delete the 33attribute from an object's dictionary. For instance, ``a.x`` has a lookup chain 34starting with ``a.__dict__['x']``, then ``type(a).__dict__['x']``, and 35continuing through the base classes of ``type(a)`` excluding metaclasses. If the 36looked-up value is an object defining one of the descriptor methods, then Python 37may override the default behavior and invoke the descriptor method instead. 38Where this occurs in the precedence chain depends on which descriptor methods 39were defined. 40 41Descriptors are a powerful, general purpose protocol. They are the mechanism 42behind properties, methods, static methods, class methods, and :func:`super()`. 43They are used throughout Python itself to implement the new style classes 44introduced in version 2.2. Descriptors simplify the underlying C-code and offer 45a flexible set of new tools for everyday Python programs. 46 47 48Descriptor Protocol 49------------------- 50 51``descr.__get__(self, obj, type=None) -> value`` 52 53``descr.__set__(self, obj, value) -> None`` 54 55``descr.__delete__(self, obj) -> None`` 56 57That is all there is to it. Define any of these methods and an object is 58considered a descriptor and can override default behavior upon being looked up 59as an attribute. 60 61If an object defines :meth:`__set__` or :meth:`__delete__`, it is considered 62a data descriptor. Descriptors that only define :meth:`__get__` are called 63non-data descriptors (they are typically used for methods but other uses are 64possible). 65 66Data and non-data descriptors differ in how overrides are calculated with 67respect to entries in an instance's dictionary. If an instance's dictionary 68has an entry with the same name as a data descriptor, the data descriptor 69takes precedence. If an instance's dictionary has an entry with the same 70name as a non-data descriptor, the dictionary entry takes precedence. 71 72To make a read-only data descriptor, define both :meth:`__get__` and 73:meth:`__set__` with the :meth:`__set__` raising an :exc:`AttributeError` when 74called. Defining the :meth:`__set__` method with an exception raising 75placeholder is enough to make it a data descriptor. 76 77 78Invoking Descriptors 79-------------------- 80 81A descriptor can be called directly by its method name. For example, 82``d.__get__(obj)``. 83 84Alternatively, it is more common for a descriptor to be invoked automatically 85upon attribute access. For example, ``obj.d`` looks up ``d`` in the dictionary 86of ``obj``. If ``d`` defines the method :meth:`__get__`, then ``d.__get__(obj)`` 87is invoked according to the precedence rules listed below. 88 89The details of invocation depend on whether ``obj`` is an object or a class. 90 91For objects, the machinery is in :meth:`object.__getattribute__` which 92transforms ``b.x`` into ``type(b).__dict__['x'].__get__(b, type(b))``. The 93implementation works through a precedence chain that gives data descriptors 94priority over instance variables, instance variables priority over non-data 95descriptors, and assigns lowest priority to :meth:`__getattr__` if provided. 96The full C implementation can be found in :c:func:`PyObject_GenericGetAttr()` in 97:source:`Objects/object.c`. 98 99For classes, the machinery is in :meth:`type.__getattribute__` which transforms 100``B.x`` into ``B.__dict__['x'].__get__(None, B)``. In pure Python, it looks 101like:: 102 103 def __getattribute__(self, key): 104 "Emulate type_getattro() in Objects/typeobject.c" 105 v = object.__getattribute__(self, key) 106 if hasattr(v, '__get__'): 107 return v.__get__(None, self) 108 return v 109 110The important points to remember are: 111 112* descriptors are invoked by the :meth:`__getattribute__` method 113* overriding :meth:`__getattribute__` prevents automatic descriptor calls 114* :meth:`object.__getattribute__` and :meth:`type.__getattribute__` make 115 different calls to :meth:`__get__`. 116* data descriptors always override instance dictionaries. 117* non-data descriptors may be overridden by instance dictionaries. 118 119The object returned by ``super()`` also has a custom :meth:`__getattribute__` 120method for invoking descriptors. The attribute lookup ``super(B, obj).m`` searches 121``obj.__class__.__mro__`` for the base class ``A`` immediately following ``B`` 122and then returns ``A.__dict__['m'].__get__(obj, B)``. If not a descriptor, 123``m`` is returned unchanged. If not in the dictionary, ``m`` reverts to a 124search using :meth:`object.__getattribute__`. 125 126The implementation details are in :c:func:`super_getattro()` in 127:source:`Objects/typeobject.c`. and a pure Python equivalent can be found in 128`Guido's Tutorial`_. 129 130.. _`Guido's Tutorial`: https://www.python.org/download/releases/2.2.3/descrintro/#cooperation 131 132The details above show that the mechanism for descriptors is embedded in the 133:meth:`__getattribute__()` methods for :class:`object`, :class:`type`, and 134:func:`super`. Classes inherit this machinery when they derive from 135:class:`object` or if they have a meta-class providing similar functionality. 136Likewise, classes can turn-off descriptor invocation by overriding 137:meth:`__getattribute__()`. 138 139 140Descriptor Example 141------------------ 142 143The following code creates a class whose objects are data descriptors which 144print a message for each get or set. Overriding :meth:`__getattribute__` is 145alternate approach that could do this for every attribute. However, this 146descriptor is useful for monitoring just a few chosen attributes:: 147 148 class RevealAccess(object): 149 """A data descriptor that sets and returns values 150 normally and prints a message logging their access. 151 """ 152 153 def __init__(self, initval=None, name='var'): 154 self.val = initval 155 self.name = name 156 157 def __get__(self, obj, objtype): 158 print('Retrieving', self.name) 159 return self.val 160 161 def __set__(self, obj, val): 162 print('Updating', self.name) 163 self.val = val 164 165 >>> class MyClass(object): 166 ... x = RevealAccess(10, 'var "x"') 167 ... y = 5 168 ... 169 >>> m = MyClass() 170 >>> m.x 171 Retrieving var "x" 172 10 173 >>> m.x = 20 174 Updating var "x" 175 >>> m.x 176 Retrieving var "x" 177 20 178 >>> m.y 179 5 180 181The protocol is simple and offers exciting possibilities. Several use cases are 182so common that they have been packaged into individual function calls. 183Properties, bound methods, static methods, and class methods are all 184based on the descriptor protocol. 185 186 187Properties 188---------- 189 190Calling :func:`property` is a succinct way of building a data descriptor that 191triggers function calls upon access to an attribute. Its signature is:: 192 193 property(fget=None, fset=None, fdel=None, doc=None) -> property attribute 194 195The documentation shows a typical use to define a managed attribute ``x``:: 196 197 class C(object): 198 def getx(self): return self.__x 199 def setx(self, value): self.__x = value 200 def delx(self): del self.__x 201 x = property(getx, setx, delx, "I'm the 'x' property.") 202 203To see how :func:`property` is implemented in terms of the descriptor protocol, 204here is a pure Python equivalent:: 205 206 class Property(object): 207 "Emulate PyProperty_Type() in Objects/descrobject.c" 208 209 def __init__(self, fget=None, fset=None, fdel=None, doc=None): 210 self.fget = fget 211 self.fset = fset 212 self.fdel = fdel 213 if doc is None and fget is not None: 214 doc = fget.__doc__ 215 self.__doc__ = doc 216 217 def __get__(self, obj, objtype=None): 218 if obj is None: 219 return self 220 if self.fget is None: 221 raise AttributeError("unreadable attribute") 222 return self.fget(obj) 223 224 def __set__(self, obj, value): 225 if self.fset is None: 226 raise AttributeError("can't set attribute") 227 self.fset(obj, value) 228 229 def __delete__(self, obj): 230 if self.fdel is None: 231 raise AttributeError("can't delete attribute") 232 self.fdel(obj) 233 234 def getter(self, fget): 235 return type(self)(fget, self.fset, self.fdel, self.__doc__) 236 237 def setter(self, fset): 238 return type(self)(self.fget, fset, self.fdel, self.__doc__) 239 240 def deleter(self, fdel): 241 return type(self)(self.fget, self.fset, fdel, self.__doc__) 242 243The :func:`property` builtin helps whenever a user interface has granted 244attribute access and then subsequent changes require the intervention of a 245method. 246 247For instance, a spreadsheet class may grant access to a cell value through 248``Cell('b10').value``. Subsequent improvements to the program require the cell 249to be recalculated on every access; however, the programmer does not want to 250affect existing client code accessing the attribute directly. The solution is 251to wrap access to the value attribute in a property data descriptor:: 252 253 class Cell(object): 254 . . . 255 def getvalue(self): 256 "Recalculate the cell before returning value" 257 self.recalc() 258 return self._value 259 value = property(getvalue) 260 261 262Functions and Methods 263--------------------- 264 265Python's object oriented features are built upon a function based environment. 266Using non-data descriptors, the two are merged seamlessly. 267 268Class dictionaries store methods as functions. In a class definition, methods 269are written using :keyword:`def` or :keyword:`lambda`, the usual tools for 270creating functions. Methods only differ from regular functions in that the 271first argument is reserved for the object instance. By Python convention, the 272instance reference is called *self* but may be called *this* or any other 273variable name. 274 275To support method calls, functions include the :meth:`__get__` method for 276binding methods during attribute access. This means that all functions are 277non-data descriptors which return bound methods when they are invoked from an 278object. In pure Python, it works like this:: 279 280 class Function(object): 281 . . . 282 def __get__(self, obj, objtype=None): 283 "Simulate func_descr_get() in Objects/funcobject.c" 284 if obj is None: 285 return self 286 return types.MethodType(self, obj) 287 288Running the interpreter shows how the function descriptor works in practice:: 289 290 >>> class D(object): 291 ... def f(self, x): 292 ... return x 293 ... 294 >>> d = D() 295 296 # Access through the class dictionary does not invoke __get__. 297 # It just returns the underlying function object. 298 >>> D.__dict__['f'] 299 <function D.f at 0x00C45070> 300 301 # Dotted access from a class calls __get__() which just returns 302 # the underlying function unchanged. 303 >>> D.f 304 <function D.f at 0x00C45070> 305 306 # The function has a __qualname__ attribute to support introspection 307 >>> D.f.__qualname__ 308 'D.f' 309 310 # Dotted access from an instance calls __get__() which returns the 311 # function wrapped in a bound method object 312 >>> d.f 313 <bound method D.f of <__main__.D object at 0x00B18C90>> 314 315 # Internally, the bound method stores the underlying function, 316 # the bound instance, and the class of the bound instance. 317 >>> d.f.__func__ 318 <function D.f at 0x1012e5ae8> 319 >>> d.f.__self__ 320 <__main__.D object at 0x1012e1f98> 321 >>> d.f.__class__ 322 <class 'method'> 323 324 325Static Methods and Class Methods 326-------------------------------- 327 328Non-data descriptors provide a simple mechanism for variations on the usual 329patterns of binding functions into methods. 330 331To recap, functions have a :meth:`__get__` method so that they can be converted 332to a method when accessed as attributes. The non-data descriptor transforms an 333``obj.f(*args)`` call into ``f(obj, *args)``. Calling ``klass.f(*args)`` 334becomes ``f(*args)``. 335 336This chart summarizes the binding and its two most useful variants: 337 338 +-----------------+----------------------+------------------+ 339 | Transformation | Called from an | Called from a | 340 | | Object | Class | 341 +=================+======================+==================+ 342 | function | f(obj, \*args) | f(\*args) | 343 +-----------------+----------------------+------------------+ 344 | staticmethod | f(\*args) | f(\*args) | 345 +-----------------+----------------------+------------------+ 346 | classmethod | f(type(obj), \*args) | f(klass, \*args) | 347 +-----------------+----------------------+------------------+ 348 349Static methods return the underlying function without changes. Calling either 350``c.f`` or ``C.f`` is the equivalent of a direct lookup into 351``object.__getattribute__(c, "f")`` or ``object.__getattribute__(C, "f")``. As a 352result, the function becomes identically accessible from either an object or a 353class. 354 355Good candidates for static methods are methods that do not reference the 356``self`` variable. 357 358For instance, a statistics package may include a container class for 359experimental data. The class provides normal methods for computing the average, 360mean, median, and other descriptive statistics that depend on the data. However, 361there may be useful functions which are conceptually related but do not depend 362on the data. For instance, ``erf(x)`` is handy conversion routine that comes up 363in statistical work but does not directly depend on a particular dataset. 364It can be called either from an object or the class: ``s.erf(1.5) --> .9332`` or 365``Sample.erf(1.5) --> .9332``. 366 367Since staticmethods return the underlying function with no changes, the example 368calls are unexciting:: 369 370 >>> class E(object): 371 ... def f(x): 372 ... print(x) 373 ... f = staticmethod(f) 374 ... 375 >>> E.f(3) 376 3 377 >>> E().f(3) 378 3 379 380Using the non-data descriptor protocol, a pure Python version of 381:func:`staticmethod` would look like this:: 382 383 class StaticMethod(object): 384 "Emulate PyStaticMethod_Type() in Objects/funcobject.c" 385 386 def __init__(self, f): 387 self.f = f 388 389 def __get__(self, obj, objtype=None): 390 return self.f 391 392Unlike static methods, class methods prepend the class reference to the 393argument list before calling the function. This format is the same 394for whether the caller is an object or a class:: 395 396 >>> class E(object): 397 ... def f(klass, x): 398 ... return klass.__name__, x 399 ... f = classmethod(f) 400 ... 401 >>> print(E.f(3)) 402 ('E', 3) 403 >>> print(E().f(3)) 404 ('E', 3) 405 406 407This behavior is useful whenever the function only needs to have a class 408reference and does not care about any underlying data. One use for classmethods 409is to create alternate class constructors. In Python 2.3, the classmethod 410:func:`dict.fromkeys` creates a new dictionary from a list of keys. The pure 411Python equivalent is:: 412 413 class Dict(object): 414 . . . 415 def fromkeys(klass, iterable, value=None): 416 "Emulate dict_fromkeys() in Objects/dictobject.c" 417 d = klass() 418 for key in iterable: 419 d[key] = value 420 return d 421 fromkeys = classmethod(fromkeys) 422 423Now a new dictionary of unique keys can be constructed like this:: 424 425 >>> Dict.fromkeys('abracadabra') 426 {'a': None, 'r': None, 'b': None, 'c': None, 'd': None} 427 428Using the non-data descriptor protocol, a pure Python version of 429:func:`classmethod` would look like this:: 430 431 class ClassMethod(object): 432 "Emulate PyClassMethod_Type() in Objects/funcobject.c" 433 434 def __init__(self, f): 435 self.f = f 436 437 def __get__(self, obj, klass=None): 438 if klass is None: 439 klass = type(obj) 440 def newfunc(*args): 441 return self.f(klass, *args) 442 return newfunc 443 444