How to create a read-only class property in Python? [duplicate] How to create a read-only class property in Python? [duplicate] python python

How to create a read-only class property in Python? [duplicate]


This will make Foo.number a read-only property:

class MetaFoo(type):    @property    def number(cls):        return cls.xclass Foo(object, metaclass=MetaFoo):    x = 4print(Foo.number)# 4Foo.number = 6# AttributeError: can't set attribute

Explanation: The usual scenario when using @property looks like this:

class Foo(object):    @property    def number(self):        ...foo = Foo()

A property defined in Foo is read-only with respect to its instances. That is, foo.number = 6 would raise an AttributeError.

Analogously, if you want Foo.number to raise an AttributeError you would need to setup a property defined in type(Foo). Hence the need for a metaclass.


Note that this read-onlyness is not immune from hackers.The property can be made writable by changing Foo'sclass:

class Base(type): passFoo.__class__ = Base# makes Foo.number a normal class attributeFoo.number = 6   print(Foo.number)

prints

6

or, if you wish to make Foo.number a settable property,

class WritableMetaFoo(type):     @property    def number(cls):        return cls.x    @number.setter    def number(cls, value):        cls.x = valueFoo.__class__ = WritableMetaFoo# Now the assignment modifies `Foo.x`Foo.number = 6   print(Foo.number)

also prints

6


The property descriptor always returns itself when accessed from a class (ie. when instance is None in its __get__ method).

If that's not what you want, you can write a new descriptor that always uses the class object (owner) instead of the instance:

>>> class classproperty(object):...     def __init__(self, getter):...         self.getter= getter...     def __get__(self, instance, owner):...         return self.getter(owner)... >>> class Foo(object):...     x= 4...     @classproperty...     def number(cls):...         return cls.x... >>> Foo().number4>>> Foo.number4


I agree with unubtu's answer; it seems to work, however, it doesn't work with this precise syntax on Python 3 (specifically, Python 3.4 is what I struggled with). Here's how one must form the pattern under Python 3.4 to make things work, it seems:

class MetaFoo(type):   @property   def number(cls):      return cls.xclass Foo(metaclass=MetaFoo):   x = 4print(Foo.number)# 4Foo.number = 6# AttributeError: can't set attribute