why descriptors? (WAS: Make staticmethod objects callable?)

Steven Bethard steven.bethard at gmail.com
Wed Mar 1 18:03:25 EST 2006


Steven Bethard wrote:
 > (For anyone else out there reading who doesn't already know this,
 > Steven D'Aprano's comments are easily explained by noting that the
 > __get__ method of staticmethod objects returns functions, and classes
 > always call the __get__ methods of descriptors when those descriptors
 > are class attributes:

Steven D'Aprano wrote:
 > Why all the indirection to implement something which is, conceptually,
 > the same as an ordinary function?

While I wasn't around when descriptors and new-style classes were 
introduced, my guess is that it's mainly because what you *usually* want 
when defining a function in a class is for that function to be an 
instance method.  That is, the following code::

     class C(object):
         def foo(self):
             pass
     c = C()
     c.foo()

should be much more common than::

     class C(object):
         def foo():
             pass
     C.foo()

because the whole point of creating a class is to allow you to create 
instances.  But if ``C.foo`` and ``c.foo`` are just regular functions, 
then how will ``c.foo()`` get the ``self`` argument?  Certainly a normal 
``foo()`` shouldn't be inserting a magical ``self`` argument.  So *some* 
indirection has to happen when a function is used in a class.

Python's solution to this problem is to introduce descriptors, which are 
the "something" that classes have to do.  All classes invoke __get__ 
whenever any of their attributes are accessed.  With a normal function 
object, invoking __get__ turns it into an instance method:

 >>> class C(object):
...     pass
...
 >>> def foo(self):
...     pass
...
 >>> foo
<function foo at 0x00E69530>
 >>> foo.__get__(C(), C)
<bound method C.foo of <__main__.C object at 0x00E738F0>>
 >>> class C(object):
...     def foo(self):
...         pass
...
 >>> C().foo
<bound method C.foo of <__main__.C object at 0x00E59C50>>

As a result, if you want to have a callable as a class attribute and you 
don't want that callable to give you an instance method when you access 
it, you can't use a regular Python function.  Personally, I think that's 
pretty reasonable since 99% of the time, I *do* want an instance method[1].

STeVe

[1] The other 1% of the time, I pretty much always want a classmethod. 
I'm still convinced that staticmethods are basically silly when I can 
just declare a module level function. ;)



More information about the Python-list mailing list