Descriptors

Table of Contents

Overview

descr.__get__(self, obj, type=None) # -> value
descr.__set__(self, obj, value)     # -> None
descr.__delete__(self, obj)         # -> None
from weakref import WeakKeyDictionary

class Grade(object):
    def __init__(self):
        self._values = WeakKeyDictionary()

    def __get__(self, instance, instance_type):
        if instance is None:
            return self
        else:
            return self._values.get(instance, 0)

    def __set__(self, instance, value):
        # Do something special
        self._values[instance] = value
class Exam(object):
    a = Grade()
    b = Grade()

Functions and Methods discussion

For classes, method definitions are just functions. For instances, these functions need to be bound to the instance. To support method calls, functions include the __get__() method for binding methods during attribute access.

class Function(object):
    . . .
    def __get__(self, obj, objtype=None):
        "Simulate func_descr_get() in Objects/funcobject.c"
        return types.MethodType(self, obj, objtype)

Data descriptors vs. non-data descriptors discussion

>>> class Descriptor(object):
...     def __init__(self, name):
...         self.name = name
...     def __get__(self, instance, cls):
...         print 'Getting %s, with instance %r, class %r' % (self.name, instance, cls)
...
>>> class Foo(object):
...     _spam = 'eggs'
...     @property
...     def spam(self):
...         return self._spam
...     @spam.setter
...     def spam(self, val):
...         self._spam = val
...
>>> Foo().spam
'eggs'
>>> foo = Foo()
>>> foo.__dict__['spam'] = Descriptor('Override')
>>> foo.spam
'eggs'