Examples of descriptors?

Michele Simionato mis6 at pitt.edu
Sat May 31 15:49:34 EDT 2003


"Edward C. Jones" <edcjones at erols.com> wrote in message news:<bba9u0$li0$1 at bob.news.rcn.net>...
> What, exactly, is a "descriptor"? The closest thing to a definition that 
> I can find is: a class that defines __get__, __set__, and __delete__. 
> Where can I find some examples?
> 
> Ed Jones

It is enough for a class to define __get__ to be a descriptor class.
Here is an example:

 ::

  #<simpledescr.py>

  class ChattyAttr(object):
      """Chatty descriptor class; descriptor objects are intended to be 
      used as attributes in other classes"""
      def __get__(self, obj, cls=None):
          binding=obj is not None
          if  binding:
              return 'You are binding %s to %s' % (self,obj)
          else:
              return 'Calling %s from %s' % (self,cls)

  class C(object):
      d=ChattyAttr()

  c=C()

  print c.d # <=> type(c).__dict__['d'].__get__(c,type(c))
  print C.d # <=> C.__dict__['d'].__get__(None,C)

  #</simpledescr.py>

with output:

 ::

  You are binding <ChattyAttr object at 0x401bc1cc> to 
  <C object at 0x401bc2ec>
  Calling <ChattyAttr object at 0x401bc1cc> from <class 'C'>

Notice that invoking a method with the syntax ``C.d`` or ``c.d`` 
involves calling ``__get__``. The ``__get__`` signature is fixed: it is
`` __get__=__get__(self,obj,cls=None)``; the notation
``self.descr_attr`` automatically passes ``self`` and ``self.__class__`` to 
``__get__``.

Custom descriptors can be used to restrict the access to objects in a
more general way than trough properties. For instance, suppose one
wants to raise an error if a given attribute 'a' is accessed, both
from the class and from the instance: a property cannot help here,
since it works only from the instance. The solution is the following
custom descriptor:

 ::

  #<oopp.py>

  class AccessError(object):
      """Descriptor raising an AttributeError when the attribute is 
      accessed""" #could be done with a property
      def __init__(self,errormessage):
          self.msg=errormessage
      def __get__(self,obj,cls=None):
          raise AttributeError(self.msg)

  #</oopp.py>
  
  >>> from oopp import AccessError
  >>> class C(object):
  ...    a=AccessError("'a' cannot be accessed")
  >>> c=C()
  >>> c.a #error
  Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    File "oopp.py", line 313, in __get__
      raise AttributeError(self.msg)
  AttributeError: 'a' cannot be accessed
  >>> C.a #error
  Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    File "oopp.py", line 313, in __get__
      raise AttributeError(self.msg)
  AttributeError: 'a' cannot be accessed

Descriptors are quite sophisticated and very little documented,
but pretty powerful and essential in order to understand how Python
works under the hood. Notice that good old functions have a __get__ method 
and therefore are descriptors too; descriptors are the mechanism that allows 
the magic conversion of functions in methods in Python classes.

If you are confused, feel free to ask for further clarifications.

Cheers,

                                    Michele




More information about the Python-list mailing list