@property; @classmethod; def f()

Steven D'Aprano steve+comp.lang.python at pearwood.info
Sun Jan 2 02:18:48 EST 2011


On Sat, 01 Jan 2011 17:55:10 -0800, K. Richard Pixley wrote:

> Can anyone explain to me why this doesn't work?
> 
> class Foo(object):
>      @property
>      @classmethod
>      def f(cls):
>          return 4

What does "doesn't work" mean? It works for me:


>>> class Foo(object):
...      @property
...      @classmethod
...      def f(cls):
...          return 4
... 
>>> 

There is no syntax error, the class is created, it works fine. What were 
you expecting to happen?


If you instantiate the class and try accessing the property, you get the 
expected runtime error:

>>> x = Foo()
>>> x.f
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'classmethod' object is not callable

(Admittedly, *you* might not have expected it, but nevertheless...)

property() expects a callable object, not a classmethod object, so 
naturally it fails. Admittedly, it's a little unexpected that 
classmethods aren't callable, but they're not:

>>> classmethod(lambda x: 42)()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'classmethod' object is not callable


Don't confuse the classmethod object with a class method (note the 
space!). Class methods are what you get once the descriptor mechanism 
kicks into action behind the scenes. classmethod objects are the 
descriptors that create class methods (note space) when required:

>>> cm = classmethod(lambda x: 42).__get__(Spam)  # descriptor protocol
>>> cm
<bound method type.<lambda> of <class 'type'>>
>>> cm()
42


> I mean, I think it seems to be syntactically clear what I'm trying to
> accomplish.  What am I missing?

An understanding of the dark and murky waters of descriptor black magic :)

http://docs.python.org/howto/descriptor.html

(It's not really that dark and murky. It's actually amazingly simple.)

My recommendation is, forget the classmethod. A combination of property 
with class attributes and self.__class__ will get you what you want.

Otherwise, just create your own descriptor object. The How To above shows 
pure-python versions of classmethod and staticmethod.


-- 
Steven



More information about the Python-list mailing list