3.8 < Python < 3.11
Can use both decorators together. See this answer.
Python 2 and python 3 (works in 3.9-3.10 too)
A property is created on a class but affects an instance. So if you want a classmethod property, create the property on the metaclass.
>>> class foo(object):
... _var = 5
... class __metaclass__(type): # Python 2 syntax for metaclasses
... pass
... @classmethod
... def getvar(cls):
... return cls._var
... @classmethod
... def setvar(cls, value):
... cls._var = value
...
>>> foo.__metaclass__.var = property(foo.getvar.im_func, foo.setvar.im_func)
>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3
But since you're using a metaclass anyway, it will read better if you just move the classmethods in there.
>>> class foo(object):
... _var = 5
... class __metaclass__(type): # Python 2 syntax for metaclasses
... @property
... def var(cls):
... return cls._var
... @var.setter
... def var(cls, value):
... cls._var = value
...
>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3
or, using Python 3's metaclass=... syntax, and the metaclass defined outside of the foo class body, and the metaclass responsible for setting the initial value of _var:
>>> class foo_meta(type):
... def __init__(cls, *args, **kwargs):
... cls._var = 5
... @property
... def var(cls):
... return cls._var
... @var.setter
... def var(cls, value):
... cls._var = value
...
>>> class foo(metaclass=foo_meta):
... pass
...
>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3
Answer from A. Coady on Stack Overflow3.8 < Python < 3.11
Can use both decorators together. See this answer.
Python 2 and python 3 (works in 3.9-3.10 too)
A property is created on a class but affects an instance. So if you want a classmethod property, create the property on the metaclass.
>>> class foo(object):
... _var = 5
... class __metaclass__(type): # Python 2 syntax for metaclasses
... pass
... @classmethod
... def getvar(cls):
... return cls._var
... @classmethod
... def setvar(cls, value):
... cls._var = value
...
>>> foo.__metaclass__.var = property(foo.getvar.im_func, foo.setvar.im_func)
>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3
But since you're using a metaclass anyway, it will read better if you just move the classmethods in there.
>>> class foo(object):
... _var = 5
... class __metaclass__(type): # Python 2 syntax for metaclasses
... @property
... def var(cls):
... return cls._var
... @var.setter
... def var(cls, value):
... cls._var = value
...
>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3
or, using Python 3's metaclass=... syntax, and the metaclass defined outside of the foo class body, and the metaclass responsible for setting the initial value of _var:
>>> class foo_meta(type):
... def __init__(cls, *args, **kwargs):
... cls._var = 5
... @property
... def var(cls):
... return cls._var
... @var.setter
... def var(cls, value):
... cls._var = value
...
>>> class foo(metaclass=foo_meta):
... pass
...
>>> foo.var
5
>>> foo.var = 3
>>> foo.var
3
Update: The ability to chain @classmethod and @property was removed in Python 3.13 .
In Python 3.9 You could use them together, but (as noted in @xgt's comment) it was deprecated in Python 3.11, so it is not longer supported (but it may work for a while or reintroduced at some point).
Check the version remarks here:
https://docs.python.org/3.11/library/functions.html#classmethod
However, it used to work like so:
class G:
@classmethod
@property
def __doc__(cls):
return f'A doc for {cls.__name__!r}'
Order matters - due to how the descriptors interact, @classmethod has to be on top.
Videos
So I have a class that tracks all of its instances in a class-level dictionary that maps the ID numbers of the class, assigned at creation, to the class objects themselves:
import uuid
class Node(object):
_all_nodes = dict()
def __init__(self):
self.id = str(uuid.uuid4())
self._all_nodes[self.id] = selfSo the class-level dictionary points at all instances of Nodes. Referencing the _all_nodes dictionary has become quite common in my code; it's the global store of data that the program is working with.
But all of the data stored in the _all_nodes class-level dictionary is actually instances of subclasses of Node, not direct instantiations of Node itself:
class Person(Node):
...
# [many methods overridden]It's frequently helpful to get a list, not of all Nodes, but of all Persons, so I wind up doing this a lot:
all_people = {k: v for k, v in Node._all_nodes.items() if isinstance(k, Person)}
# do something with all_peopleIn fact, I do it often enough that recreating that line has stopped involving thinking about anything other than how tedious it is to re-type, or hunt down again to copy and paste. It seems like the obvious thing to do is to bundle it into the Person class:
class Person(Node):
def _all_people(self):
return {k: v for k, v in Node._all_nodes.items() if isinstance(k, Person)}The inconvenience of having to remember that Person._all_people is a function call (as are similar definitions in other subclasses), while Node._all_nodes refers directly to an attribute of an object, can be ameliorated by making _all_people a property to obscure the fact that it's a function call:
class Person(Node):
@property
def _all_people(self):
return {k: v for k, v in Node._all_nodes.items() if isinstance(k, Person)}This works, but there's a larger problem that prevents it from being useful: it requires an instance, rather than just the name of the class, in order to get access to the _all_people attribute. But if I really want a list of _all_people, I'm probably working on a higher-level task and don't happen to have an instance of Person ready to hand so that I can examine the property's returned value!
What I'd really like is to make the _all_people property callable from the class definition, without an instance, like it is with Node. But when I try this:
class Person(Node):
@classmethod
@property
def _all_people(cls):
return {p: cls._all_nodes[p] for p in cls._all_nodes if isinstance(cls._all_nodes[p], Person)}I get code that runs, but doesn't effectively access the data: p = Person(); print(p._all_people) prints, not the dictionary, but rather <bound method ? of <class '__main__.Person'>>, which is not at all helpful.
Inverting the order of the @property and @classmethod decorators gives me the error TypeError: 'classmethod' object is not callable.
Is there some productive way that I can use @property and @classmethod to decorate the same method?
This is Python 3.5 under x64 Linux.
Here's how I would do this:
class ClassPropertyDescriptor(object):
def __init__(self, fget, fset=None):
self.fget = fget
self.fset = fset
def __get__(self, obj, klass=None):
if klass is None:
klass = type(obj)
return self.fget.__get__(obj, klass)()
def __set__(self, obj, value):
if not self.fset:
raise AttributeError("can't set attribute")
type_ = type(obj)
return self.fset.__get__(obj, type_)(value)
def setter(self, func):
if not isinstance(func, (classmethod, staticmethod)):
func = classmethod(func)
self.fset = func
return self
def classproperty(func):
if not isinstance(func, (classmethod, staticmethod)):
func = classmethod(func)
return ClassPropertyDescriptor(func)
class Bar(object):
_bar = 1
@classproperty
def bar(cls):
return cls._bar
@bar.setter
def bar(cls, value):
cls._bar = value
# test instance instantiation
foo = Bar()
assert foo.bar == 1
baz = Bar()
assert baz.bar == 1
# test static variable
baz.bar = 5
assert foo.bar == 5
# test setting variable on the class
Bar.bar = 50
assert baz.bar == 50
assert foo.bar == 50
The setter didn't work at the time we call Bar.bar, because we are calling
TypeOfBar.bar.__set__, which is not Bar.bar.__set__.
Adding a metaclass definition solves this:
class ClassPropertyMetaClass(type):
def __setattr__(self, key, value):
if key in self.__dict__:
obj = self.__dict__.get(key)
if obj and type(obj) is ClassPropertyDescriptor:
return obj.__set__(self, value)
return super(ClassPropertyMetaClass, self).__setattr__(key, value)
# and update class define:
# class Bar(object):
# __metaclass__ = ClassPropertyMetaClass
# _bar = 1
# and update ClassPropertyDescriptor.__set__
# def __set__(self, obj, value):
# if not self.fset:
# raise AttributeError("can't set attribute")
# if inspect.isclass(obj):
# type_ = obj
# obj = None
# else:
# type_ = type(obj)
# return self.fset.__get__(obj, type_)(value)
Now all will be fine.
If you define classproperty as follows, then your example works exactly as you requested.
class classproperty(object):
def __init__(self, f):
self.f = f
def __get__(self, obj, owner):
return self.f(owner)
The caveat is that you can't use this for writable properties. While e.I = 20 will raise an AttributeError, Example.I = 20 will overwrite the property object itself.
Suppose I had the class `vehicle` which represents a motor vehicle. Suppose the horsepower of the vehicle was not passed as an inputs but, with some detailed calculation, could be calculated from the other properties of the vehicle class. Would it be better to add `horsepower` as a property of the `vehicle` class, or as a method?
As a property, this might look something like this:
class Vehicle:
def __init__(self, args):
# Set args
self._horsepower = None
@property
def horsepower(self):
if self._horsepower is None:
self._horsepower = calculate_horsepower()
return self._horsepowerAs a method, it may look like this:
class Vehicle:
def __init__(self, args):
# Set args
def calculate_horsepower(self):
# Calculate horsepower of instance vehicleWhich of the above is preferable?
In reality, horsepower is a property of a vehicle. However, if significant processing is required to calculate it then I'm not sure if it feels right to have it as a property of the `vehicle` class.