pre-PEP generic objects

Jacek Generowicz jacek.generowicz at cern.ch
Mon Dec 6 10:09:25 CET 2004


Jp Calderone <exarkun at divmod.com> writes:

> When the class object is created, the namespace is scanned for
> instances of <type 'function'>.  For those and only those, a
> descriptor is created which will produce bound and unbound methods.

This isn't quite correct. A descriptior is any object which has
callable attributes called "__get__", "__set__" or "__delete__". Pure
Python functions *are* descriptors:

>>> import types
>>> for name in '__get__', '__set__', '__delete__':
...     print name, name in dir(types.FunctionType)
... 
__get__ True
__set__ False
__delete__ False


because they have a __get__ attribute. Builtin functions are not
descriptors:

>>> for name in '__get__', '__set__', '__delete__':
...     print name, name in dir(types.BuiltinFunctionType)
... 
__get__ False
__set__ False
__delete__ False

because they do not have any of these.


It is the FunctionType.__get__ which is responsible for the binding of
methods. You can see this for yourself by calling it directly:

>>> class Foo(object):
...     pass
... 
>>> def bar():
...     pass
... 
>>> bar.__get__(None, Foo)
<unbound method Foo.bar>
>>> bar.__get__(Foo(), Foo)
<bound method Foo.bar of <__main__.Foo object at 0x40171a6c>>

Note that if FunctionType.__get__ is passed both an instance and a
class, then it returns a bound method. If it is only passed a class,
then it returns an unbound method.

So, let's write a little descriptior which merely reports what
arguments it's __get__ method receives:

>>> class Test__get__(object):
...     def __get__(*args):
...             return args
... 

Let's add an instance of this as a class attribute to Foo:

>>> Foo.test = Test__get__()

... and see what happens when you access it through the class:

>>> Foo.test
(<__main__.Test__get__ object at 0x40171b8c>, None, <class '__main__.Foo'>)

... and what happens when you access it via an instance of the class:

>>> Foo().test
(<__main__.Test__get__ object at 0x40171b8c>, <__main__.Foo object at 0x40171a4c>, <class '__main__.Foo'>)

Observe that when accessing the attribute through the class, __get__
is not passed any instance, which tells __get__ to return an unbound
method (as we saw above). If it is accessed through an instance,
__get__ is passed both an instance and its class, which tells __get__
to return a bound method.

The question remains "Why is __get__ called when an attribute is
looked up?" The answer is "Because that's what __getattribute__
does". Which __getattribute__? That depends. If you access via the
class, then it's type.__getattribute__, if you access through an
instance of the class, then it's object.__getattribute__. More
generally, an attribute access such as

    X.Y

is equivalent to

   type(X).__getattribute__('Y')

So, the type of object whose attribute you are looking up, determines
(via it's __getattribute__ method) the arguments that the attribute's
__get__ will be passed.

Note that __getattribute__ of ModuleType, does *not* invoke this
descriptor protocol of functions at all ... otherwise, all
module-level functions would be turned into methods !



More information about the Python-list mailing list