As of Python version 3.9, it is possible to define a class property (i.e. a computed or otherwise managed class attribute) by simply stacking the classmethod and property decorators.

For example:

class Class:
    @classmethod
    @property
    def reversed_mro(cls):
        return cls.mro()[::-1]


class SubClass(Class):
    ...

Accessing the attribute for reading works as expected in Python 3.9:

>>> # via class
>>> Class.reversed_mro
[<class 'object'>, <class '__main__.Class'>]
>>> SubClass.reversed_mro
[<class 'object'>, <class '__main__.Class'>, <class '__main__.SubClass'>]

>>> # via instance
>>> Class().reversed_mro
[<class 'object'>, <class '__main__.Class'>]
>>> SubClass().reversed_mro
[<class 'object'>, <class '__main__.Class'>, <class '__main__.SubClass'>]

as opposed to version 3.8 or earlier:

>>> Class.reversed_mro
<bound method ? of <class '__main__.Class'>>
>>> SubClass.reversed_mro
<bound method ? of <class '__main__.SubClass'>>

This change to the language was introduced as the result of this issue, with class properties being mentioned in the discussion as one of the potential benefits of it.

However, carrying on with the above example we will soon discover something interesting: it is possible to set and delete the property attribute without any restrictions:

>>> Class.reversed_mro = []
>>> Class.reversed_mro
[]
>>> del Class.reversed_mro
>>> hasattr(Class, 'reversed_mro')
False

This is not consistent with the behaviour of a regular instance property whose setter and deleter methods are not explicitly defined. For example:

class Foo:
    @property
    def reversed_mro(self):
        return type(self).mro()[::-1]
>>> instance = Foo()
>>> instance.reversed_mro
[<class 'object'>, <class '__main__.Foo'>]
>>> instance.reversed_mro = []
AttributeError: can't set attribute
>>> del instance.reversed_mro
AttributeError: can't delete attribute

It is unclear to me whether this inconsistency was foreseen and/or intended by the Python core developers. This unit test was added to Python’s test suite as part of the change, but it is just a simple test that doesn’t attempt to set or delete the attribute. It seems that this part of behaviour of a class property (i.e. the setting and deleting part of its descriptor protocol) may have not been taken into account at all.

Update 7 September 2022

In the What’s New In Python 3.11 document, the possibility of decorating property with classmethod is announced to be deprecated.

Chaining classmethod descriptors (introduced in bpo-19072) is now deprecated. It can no longer be used to wrap other descriptors such as property. The core design of this feature was flawed and caused a number of downstream problems.

Update 12 June 2024

In Python 3.13, support for stacking the classmethod and property decorators is removed.

Removed chained classmethod descriptors (introduced in gh-63272) … To “pass-through” a classmethod, consider using the __wrapped__ attribute that was added in Python 3.10.

The behaviour is now consistent with that of Python 3.8 and earlier:

>>> Class.reversed_mro
<bound method reversed_mro of <class '__main__.Class'>>
>>> SubClass.reversed_mro
<bound method reversed_mro of <class '__main__.SubClass'>>