views:

97

answers:

1

The following doesn't work for some reason:

>>> class foo(object):
...     @property
...     @classmethod
...     def bar(cls):
...             return "asdf"
... 
>>> foo.bar
<property object at 0x1da8d0>
>>> foo.bar + '\n'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'property' and 'str'

Is there a way to do this, or is my only alternative to resort to some kind of metaclass trickery?

+5  A: 

If you want the descriptor property to trigger when you get an attribute from object X, then you must put the descriptor in type(X). So if X is a class, the descriptor must go in the class's type, also known as the class's metaclass -- no "trickery" involved, it's just a matter of completely general rules.

Alternatively, you might write your own special-purpose descriptor. See here for an excellent "how-to" treaty on descriptors. Edit for example:

class classprop(object):
  def __init__(self, f):
    self.f = classmethod(f)
  def __get__(self, *a):
    return self.f.__get__(*a)()

class buh(object):
  @classprop
  def bah(cls): return 23

print buh.bah

emits 23, as desired.

Alex Martelli
I'm using a metaclass elsewhere in the inheritance hierarchy, so if I use the metaclass, I get the following: `metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases`
Jason Baker
*However*, the decorator worked like a charm. Thanks!
Jason Baker
@Jason, you could also use a subclass of all involved metaclasses, as the error message says (few use multiple custom metaclasses, or metaclasses that don't subclass `type`, so probably you only need to subclass the one custom metaclass you're using) -- but if the custom descriptor does the job, it may indeed be a simpler approach.
Alex Martelli