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 Overflow
Top answer
1 of 16
201

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
2 of 16
171

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.

🌐
Reddit
reddit.com › r/learnpython › @property with @classmethod ?
r/learnpython on Reddit: @property with @classmethod ?
May 1, 2020 -

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] = self

So 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_people

In 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.

🌐
GitHub
github.com › python › cpython › issues › 89519
Calling `help` executes @classmethod @property decorated methods · Issue #89519 · python/cpython
August 10, 2021 - assignee = 'https://github.com/rhettinger' closed_at = None created_at = <Date 2021-10-03.19:45:55.963> labels = ['type-bug', '3.11'] title = 'Calling `help` executes @classmethod @property decorated methods' updated_at = <Date 2022-02-17.20:03:40.349> user = 'https://github.com/randolf-scholz' bugs.python.org fields: activity = <Date 2022-02-17.20:03:40.349> actor = 'rhettinger' assignee = 'rhettinger' closed = False closed_date = None closer = None components = [] creation = <Date 2021-10-03.19:45:55.963> creator = 'randolf.scholz' dependencies = [] files = ['50325', '50326', '50344'] hgrepo
Author   randolf-scholz
🌐
Medium
elfi-y.medium.com › python-properties-and-class-methods-a6c7ad69b0f1
Python Properties and Class Methods | by E.Y. | Medium
January 10, 2021 - class MyClass: def instance_method(self): return 'instance method called', self @classmethod def classmethod(cls): return 'class method called', cls @staticmethod def staticmethod(): return 'static method called' Instance Methods: The first methodmethod is an instance method. The parameter selfpoints to an instance of MyClass. It can also access the class itself through self.__class__ property.
🌐
Hrekov
hrekov.com › blog › python-property-vs-classmethod
Python's @property vs. @classmethod - A No-Nonsense Guide | Backend APIs, Web Apps, Bots & Automation | Hrekov
July 22, 2025 - Without @property, you'd have to define get_diameter() and call it as c.get_diameter(). The decorator just makes your class's interface cleaner. The @classmethod, on the other hand, is about methods that are bound to the class itself, not to a specific instance of the class.
🌐
Programiz
programiz.com › python-programming › property
Python @property Decorator (With Examples)
In this tutorial, you will learn about Python @property decorator; a pythonic way to use getters and setters in object-oriented programming.
🌐
Python
bugs.python.org › issue20659
Issue 20659: Want to make a class method a property by combining decorators - Python tracker
This issue tracker has been migrated to GitHub, and is currently read-only. For more information, see the GitHub FAQs in the Python's Developer Guide · This issue has been migrated to GitHub: https://github.com/python/cpython/issues/64858
🌐
Real Python
realpython.com › python-property
Python's property(): Add Managed Attributes to Your Classes – Real Python
December 15, 2024 - It allows you to control attribute access, enabling features such as data validation, lazy evaluation, and the creation of backward-compatible APIs without modifying the class’s public interface. By using @property, you can avoid the clutter of getter and setter methods, keeping your code clean and Pythonic.
Find elsewhere
Top answer
1 of 9
137

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.

2 of 9
73

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.

🌐
Rednafi
rednafi.com › python › access 'classmethod's like 'property' methods in python
Access 'classmethod's like 'property' methods in Python | Redowan's Reflections
December 29, 2025 - While sifting through Django 3.2’s codebase, I found this neat trick to make a classmethod that acts like a property method and can be accessed directly from the class without initializing it. # src.py # This requires Python 3.4+. from enum import Enum, EnumMeta class PlanetsMeta(EnumMeta): @property def choices(cls): return [(v.name, v.value) for v in cls] class Planets(Enum, metaclass=PlanetsMeta): EARTH = "earth" MARS = "mars" # This can be accessed as follows.
🌐
Python documentation
docs.python.org › 3 › howto › descriptor.html
Descriptor Guide — Python 3.14.3 documentation
Descriptors are used throughout the language. It is how functions turn into bound methods. Common tools like classmethod(), staticmethod(), property(), and functools.cached_property() are all implemented as descriptors.
🌐
GitHub
github.com › microsoft › pyright › issues › 1423
Classmethod properties in Python 3.9 · Issue #1423 · microsoft/pyright
September 13, 2020 - Describe the bug In Python 3.9, it's now possible to chain decorators with @classmethod. However, pyright raises an error when running the example code given in the Python docs. ... class G: @classmethod @property def __doc__(cls) -> str: return f"A doc for {cls.__name__!r}" print(G.__doc__)
Author   kosayoda
🌐
Python
bugs.python.org › issue45356
Issue 45356: Calling `help` executes @classmethod @property decorated methods - Python tracker
This issue tracker has been migrated to GitHub, and is currently read-only. For more information, see the GitHub FAQs in the Python's Developer Guide · This issue has been migrated to GitHub: https://github.com/python/cpython/issues/89519
🌐
GitHub
github.com › zarr-developers › numcodecs › issues › 553
classmethod properties are removed in Python 3.13 · Issue #553 · zarr-developers/numcodecs
July 21, 2024 - @classmethod · @property · def max_level(cls): """Returns the maximum compression level of the underlying zstd library.""" return ZSTD_maxCLevel() However, as of Python 3.11, using classmethod on property was deprecated, and has been removed in Python 3.13: https://docs.python.org/3.13/library/functions.html#classmethod ·
Author   QuLogic
🌐
Python.org
discuss.python.org › python help
Abstract, generic class properties beyond Python 3.13 - Python Help - Discussions on Python.org
May 13, 2024 - Up until now (Python 3.12) I’ve been using this pattern to get the concrete type out of an abstract generic at run time: DataIn = TypeVar('DataIn', infer_variance=True) class Handler(Generic[DataIn], ABC): @classmethod @property def processable(cls) -> type[DataIn]: return get_args(get_o...
🌐
JetBrains
youtrack.jetbrains.com › issue › PY-47615
Support combining @classmethod and @property
{{ (>_<) }} This version of your browser is not supported. Try upgrading to the latest stable version. Something went seriously wrong
🌐
Tush
tush.ar › post › descriptors
Implementing @property, @staticmethod and @classmethod from scratch
To work around this, Python actually creates a new descriptor object. So, we replace the old x (which only had a getter function) with a new x (which has both getter and setter functions). Like so: class my_property: def __init__(self, getter_func, setter_func=None): self.getter_func = getter_func self.setter_func = setter_func def __get__(self, obj, cls): return self.getter_func(obj) def setter(self, setter_func): return my_property(self.getter_func, setter_func) def __set__(self, obj, value): return self.setter_func(obj, value) class C: def __init__(self) -> None: self._x = 42 @my_property def x(self): return self._x @x.setter def x(self, new_value): if new_value < self._x: raise ValueError("new value must be bigger than old one") self._x = new_value
🌐
GitHub
github.com › python › mypy › issues › 13746
0.981 `@classmethod` and `@property` can no longer be chained · Issue #13746 · python/mypy
August 10, 2022 - Class properties were introduced in Python 3.9, and used to be unanalysed in 0.971: # mypy 0.971 class A: @property @classmethod def bad_class_property(cls) -> int: # No mypy error return 8 @classmethod @property def good_class_property(...
Author   bzoracler
Top answer
1 of 3
12

Like I always did before 3.9, nonetheless: a custom "property" rewrite.

The problem is, "property" does a lot of things, and if one will need everything its in there, it is a lot of code.

I guess it is possible to just subclass property itself, so that we can get an extra .class_getter decorator.

A class setter, obviously, would involve either a custom metaclass or an especialisation of __setattr__.

Let's see if I can come with a reasonably short classproperty.

[after tinkering a bit]

So, it turns out simply inheriting property and adding a decorator for a "class getter" is not easily feasible - "property" is not written with subclassing and expanding its functionality in mind.

Therefore, the "easy" thing, and subset is to write a custom descriptor decorator, which will just convert a single method into a classgetter - and no set, del or inheritance support at all.

On the other hand, the code is short and simple:

class classproperty:
    def __init__(self, func):
        self.fget = func
    def __get__(self, instance, owner):
        return self.fget(owner)

And this simply works as expected:


In [19]: class A:
    ...:     @classproperty
    ...:     def test(cls):
    ...:         return f"property of {cls.__name__}"
    ...: 

In [20]: A.test
Out[20]: 'property of A'

Another way, if one wants to go all the way to have a class attribute setter, it is a matter of writing a plain property on a custom metaclass (which can exist just for holding the desired properties). This approach however will render the properties invisible on the instances - they will work only on the class itself:


In [22]: class MetaA(type):
    ...:     @property
    ...:     def test(cls):
    ...:         return cls._test
    ...:     @test.setter
    ...:     def test(cls, value):
    ...:         cls._test = value.upper()
    ...: 

In [23]: class A(metaclass=MetaA):
    ...:     pass
    ...: 

In [24]: A.test = "hello world!"

In [25]: A.test
Out[25]: 'HELLO WORLD!'

In [26]: A().test
--------------------------------------------------------------
...
AttributeError: 'A' object has no attribute
2 of 3
0

I think this is a very good question, and I wish it had a better answer. My favourite approach that I was able to find was using __init_subclass__ from this answer.

🌐
Python
bugs.python.org › issue44973
Issue 44973: @classmethod can be stacked on @property, but @staticmethod cannot - Python tracker
This issue tracker has been migrated to GitHub, and is currently read-only. For more information, see the GitHub FAQs in the Python's Developer Guide · This issue has been migrated to GitHub: https://github.com/python/cpython/issues/89136