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.

Discussions

python - How to make a class property? - Stack Overflow
In python I can add a method to a class with the @classmethod decorator. Is there a similar decorator to add a property to a class? I can better show what I'm talking about. class Example(object... More on stackoverflow.com
🌐 stackoverflow.com
classmethod properties are removed in Python 3.13
Problem description This was added in #519: numcodecs/numcodecs/zstd.pyx Lines 261 to 277 in 696e582 @classmethod @property def default_level(cls): """Returns the default compression level of the u... More on github.com
🌐 github.com
3
July 21, 2024
Abstract, generic class properties beyond Python 3.13
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... More on discuss.python.org
🌐 discuss.python.org
0
1
May 13, 2024
Classmethod properties in Python 3.9
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. To Reproduce Code: class G: @classmethod @property def __do... More on github.com
🌐 github.com
1
January 26, 2021
🌐
GitHub
github.com › python › cpython › issues › 89519
Calling `help` executes @classmethod @property decorated methods · Issue #89519 · python/cpython
August 6, 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.
🌐
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.

🌐
Python
bugs.python.org › issue20659
Issue 20659: Want to make a class method a property by combining decorators - Python tracker
February 17, 2014 - 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
🌐
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
January 30, 2026 - 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 › 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...
🌐
GitHub
github.com › microsoft › pyright › issues › 1423
Classmethod properties in Python 3.9 · Issue #1423 · microsoft/pyright
January 26, 2021 - 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
🌐
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
🌐
GitHub
github.com › python › mypy › issues › 13746
0.981 `@classmethod` and `@property` can no longer be chained · Issue #13746 · python/mypy
September 27, 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.

🌐
Tush
tush.ar › post › descriptors
Implementing @property, @staticmethod and @classmethod from scratch
March 17, 2022 - 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
🌐
Better Programming
betterprogramming.pub › how-to-use-the-magical-staticmethod-classmethod-and-property-decorators-in-python-e42dd74e51e7
How to Use the Magical @staticmethod, @classmethod, and @property Decorators in Python
August 25, 2023 - We will introduce three special decorators in Python, namely @staticmethod, @classmethod, and @propery which can make your code cleaner and more professional