[answer written based on python 3.4; the metaclass syntax is different in 2, but I think the technique will work anyway]
You can do this with a metaclass ... basically. Dappavit almost works, but I think it has a flaw:
class MetaFoo(type): @property def thingy(cls): return cls._thingy class Foo(object, metaclass=MetaFoo): _thingy = 23
This gives you a cool property in Foo, but there is a problem ...
print("Foo.thingy is {}".format(Foo.thingy)) # Foo.thingy is 23 # Yay, the classmethod-property is working as intended! foo = Foo() if hasattr(foo, "thingy"): print("Foo().thingy is {}".format(foo.thingy)) else: print("Foo instance has no attribute 'thingy'") # Foo instance has no attribute 'thingy' # Wha....?
What the hell is going on here? Why can't I get the class property from the instance?
I banged my head about this for quite some time before finding what I thought was the answer. Python @properties is a subset of descriptors, and from the descriptive documentation (my selection):
The default behavior for accessing attributes is to get, set, or remove an attribute from an object dictionary. For example, ax has a search chain starting with a.__dict__['x'] , then type(a).__dict__['x'] and continuing through the base classes of type(a) excluding metaclasses .
Thus, the method resolution order does not include our class properties (or anything else defined in the metaclass). You can subclass the built-in property decorator, which behaves differently, but (quote). I have the impression that the developers have a good reason (which I donβt understand) for this.
This does not mean that we are out of luck; we can access the properties of the class itself simply ... and we can get the class from type(self) inside the instance, which we can use to create @property dispatchers:
class Foo(object, metaclass=MetaFoo): _thingy = 23 @property def thingy(self): return type(self).thingy
Now Foo().thingy works for both class and instances! It will also continue to do the right thing if the derived class replaces its base _thingy (which is the use case that originally got me on this hunt).
This is not 100% satisfactory to me - to make adjustments both in the metaclass and in the object class, it seems that this violates the DRY principle. But the latter is just a single line dispatcher; I basically agree with this, and you could probably squeeze it to lambda or something else if you really wanted to.