3

(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:

  • An answer which points out that chaining @classmethod and @property has been disabled since Python 3.13
  • Another solution which defines a customized classproperty 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
    
  • A solution by writing the class property into metaclass. This method successfully prevents attempted modifications, but with this method, access to class variables will only be possible via class, not via instance.
    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?

  • Can prevent attempted modifications
  • Can be accessed from both class and instance
2
  • Would a solution involving descriptors + a fallback in getattr for instance access be acceptable, or are you aiming strictly for native @property-like syntax?
    – Adel Alaa
    Commented 12 hours ago
  • "What is the ultimate solution for class property?" define your own bespoke descriptor Commented 2 hours ago

1 Answer 1

4

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

1
  • Given so, then is there a way to regularize the subclasses of a specific class that they should all have a class property with a specific name, just as what ABC has done to a class's instances?
    – F. X. P.
    Commented 12 hours ago

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.