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