(I know that there are similar questions already answered, but my question focuses more on the reason behind the solution instead of the solution itself).
I have been in need of something like a "class property" in Python, and I have searched through existing questions. Some answers provide a workaround, but I cannot understand why python disabled chaining @classmethod and @property. Is there any explanation for this?
Also, I've found that all the currently available solutions have limitations, which are listed below. The posts I have read include:
@classmethod and @property has been disabled since Python 3.13classproperty descriptor. But this workaround fails to prevent modification. For example, the following code derived from the original answer will not raise an exception when modification over x is attempted.
class classproperty(property):
def __get__(self, owner_self, owner_cls):
return self.fget(owner_cls)
def __set__(self, instance, value):
raise AttributeError("can't set attribute")
class C(object):
@classproperty
def x(cls):
return 1
print(C.x)
C.x = 2
print(C.x) # Output: 2
# no exception raised
# cannot prevent modification
class CMeta(type):
@property
def x(cls):
return 1
class C(object, metaclass=CMeta): ...
print(C.x)
# C.x = 2
# AttributeError: property 'x' of 'CMeta' object has no setter
# print(C().x)
# AttributeError: 'C' object has no attribute 'x'
So is there an ultimate way to resolve all the above mentioned problems and allow for a class property implementation satisfying the following two conditions?
Class property was deprecated in Python 3.11 (with link to the original issues) because it was found to be impossible for a chain of @classmethod and @property-decorated attribute to be seen by inspection code as an instance of property.
If you need a class property to work on an instance then a custom classproperty is the way to go, but to prevent modifications you can override the __setattr__ method of the metaclass so that it raises an exception if the named attribute is found to be an instance of classproperty:
class classproperty(property):
def __get__(self, owner_self, owner_cls):
return self.fget(owner_cls)
class CMeta(type):
def __setattr__(cls, name, value):
if isinstance(vars(cls).get(name), classproperty):
raise AttributeError("can't set attribute")
super().__setattr__(name, value)
class C(metaclass=CMeta):
@classproperty
def x(cls):
return 1
print(C.x) # 1
print(C().x) # 1
C.x = 2 # AttributeError: can't set attribute
Demo: https://ideone.com/4Ys77N
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With