views:

89

answers:

2

Essentially I want to do something like this:

class foo:
    x = 4
    @property
    @classmethod
    def number(cls):
        return x

Then I would like the following to work:

>>> foo.number
4

Unfortunately, the above doesn't work. Instead of given me 4 it gives me <property object at 0x101786c58>. Is there any way to achieve the above?

+8  A: 

Edit: I rather like bobince's solution (and hope it becomes the accepted solution) since it does not involve metaclasses, and believe things that can be done just as simply without metaclasses probably should be.

class MetaFoo(type):
    @property
    def number(cls):
        return cls.x

class Foo(object):
    __metaclass__=MetaFoo
    x = 4

print(Foo.number)
# 4

The usual scenario when using @property is this:

class Foo(object):
    @property
    def number(self):
        ...
foo=Foo()

foo.number causes Python to look for number in foo.__class__, that is, Foo, and if it is decorated with @property, Python calls the function decorated by @property, with foo as its first argument.

In complete analogy to this situation, if you want Foo.number to call a function, you could setup

class MetaFoo(type):
    @property
    def number(cls):
        return cls.x

class Foo(object):
    __metaclass__=MetaFoo
    x = 4

Just as before, Foo.number causes Python to look for number in Foo.__class, that is, MetaFoo and since it is decorated with @property, Python calls the function so decorated with Foo as its first argument.

unutbu
+6  A: 

The property descriptor always returns itself when accessed from a class (ie. when instance is None in its __get__ method).

If that's not what you want, you can write a new descriptor that always uses the class object (owner) instead of the instance:

>>> class classproperty(object):
...     def __init__(self, getter):
...         self.getter= getter
...     def __get__(self, instance, owner):
...         return self.getter(owner)
... 
>>> class Foo(object):
...     x= 4
...     @classproperty
...     def number(cls):
...         return cls.x
... 
>>> Foo().number
4
>>> Foo.number
4
bobince