[ACCEPTED]-Class-level read-only properties in Python-python

Accepted answer
Score: 10

The existing solutions are a bit complex 12 -- what about just ensuring that each class 11 in a certain group has a unique metaclass, then 10 setting a normal read-only property on the 9 custom metaclass. Namely:

>>> class Meta(type):
...   def __new__(mcl, *a, **k):
...     uniquemcl = type('Uniq', (mcl,), {})
...     return type.__new__(uniquemcl, *a, **k)
... 
>>> class X: __metaclass__ = Meta
... 
>>> class Y: __metaclass__ = Meta
... 
>>> type(X).foo = property(lambda *_: 23)
>>> type(Y).foo = property(lambda *_: 45)
>>> X.foo
23
>>> Y.foo
45
>>> 

this is really 8 much simpler, because it's based on nothing 7 more than the fact that when you get an 6 instance's attribute descriptors are looked 5 up on the class (so of course when you get 4 a class's attribute descriptors are looked 3 on the metaclass), and making class/metaclass 2 unique isn't terribly hard.

Oh, and of course:

>>> X.foo = 67
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

just 1 to confirm it IS indeed read-only!

Score: 5

The ActiveState solution that Pynt references makes instances 14 of ROClass have read-only attributes. Your 13 question seems to ask if the class itself 12 can have read-only attributes.

Here is one 11 way, based on Raymond Hettinger's comment:

#!/usr/bin/env python
def readonly(value):
    return property(lambda self: value)

class ROType(type):
    CLASS_PROPERTY = readonly(1)

class Foo(object):
    __metaclass__=ROType

print(Foo.CLASS_PROPERTY)
# 1

Foo.CLASS_PROPERTY=2
# AttributeError: can't set attribute

The idea is this: Consider 10 first Raymond Hettinger's solution:

class Bar(object):
    CLASS_PROPERTY = property(lambda self: 1)
bar=Bar()
bar.CLASS_PROPERTY=2

It shows 9 a relatively simple way to give bar a read-only 8 property.

Notice that you have to add the 7 CLASS_PROPERTY = property(lambda self: 1) line to the definition of the class of 6 bar, not to bar itself.

So, if you want the 5 class Foo to have a read-only property, then 4 the parent class of Foo has to have CLASS_PROPERTY = property(lambda self: 1) defined.

The 3 parent class of a class is a metaclass. Hence 2 we define ROType as the metaclass:

class ROType(type):
    CLASS_PROPERTY = readonly(1)

Then we 1 make Foo's parent class be ROType:

class Foo(object):
    __metaclass__=ROType
Score: 0

Found this on ActiveState:

# simple read only attributes with meta-class programming

# method factory for an attribute get method
def getmethod(attrname):
    def _getmethod(self):
        return self.__readonly__[attrname]

    return _getmethod

class metaClass(type):
    def __new__(cls,classname,bases,classdict):
        readonly = classdict.get('__readonly__',{})
        for name,default in readonly.items():
            classdict[name] = property(getmethod(name))

        return type.__new__(cls,classname,bases,classdict)

class ROClass(object):
    __metaclass__ = metaClass
    __readonly__ = {'a':1,'b':'text'}


if __name__ == '__main__':
    def test1():
        t = ROClass()
        print t.a
        print t.b

    def test2():
        t = ROClass()
        t.a = 2

    test1()

Note that if you try to set 2 a read-only attribute (t.a = 2) python will raise 1 an AttributeError.

More Related questions