Read-only class properties

Bengt Richter bokr at oz.net
Mon Jul 11 11:30:37 EDT 2005


On Sun, 10 Jul 2005 21:10:36 -0700, Michael Spencer <mahs at telcopartners.com> wrote:

>Bengt Richter wrote:
>...
>> 
>> class Foo(object):
>>     class __metaclass__(type):
>>         def __setattr__(cls, name, value):
>>             if type(cls.__dict__.get(name)).__name__ == 'Descriptor':
>>                 raise AttributeError, 'setting Foo.%s to %r is not allowed' %(name, value) 
>>             type.__setattr__(cls, name, value)
>>     @classproperty
>>     def TheAnswer(cls):
>>         return "The Answer according to %s is 42" % cls.__name__
>>     @classproperty
>>     def AnotherAnswer(cls):
>>         return "Another Answer according to %s is 43" % cls.__name__
>> 
>
>or, simply put the read-only descriptor in the metaclass:
>
>  Python 2.4 (#60, Nov 30 2004, 11:49:19) [MSC v.1310 32 bit (Intel)] on win32
>  Type "help", "copyright", "credits" or "license" for more information.
>  >>> def classproperty(function):
>  ...     class Descriptor(object):
>  ...         def __get__(self, obj, objtype):
>  ...            return function(objtype)
>  ...         def __set__(self, obj, value):
>  ...             raise AttributeError, "can't set class attribute"
>  ...     return Descriptor()
>  ...
>  >>> class A(object):
>  ...     class __metaclass__(type):
>  ...         @classproperty
>  ...         def TheAnswer(cls):
>  ...             return "The Answer according to %s is 42" % cls.__name__
>  ...
>  >>> A.TheAnswer
>  'The Answer according to __metaclass__ is 42'
>  >>> A.TheAnswer = 3
>  Traceback (most recent call last):
>    File "<input>", line 1, in ?
>    File "<input>", line 6, in __set__
>  AttributeError: can't set class attribute
>  >>> class B(A): pass
>  ...
>  >>> B.TheAnswer
>  'The Answer according to __metaclass__ is 42'
>  >>>
>
>
>this means that the getter doesn't automatically get a reference to the class 
>(since it is a method of metaclass), which may or may not matter, depending on 
>the application
>
It appears that you can use an ordinary property in the metaclass, and get the reference:
(I tried doing this but I still had the classproperty decorator and somehow inside a metaclass
it bombed or I typoed, and I forgot to try the plain property, so I hacked onwards to the
more involved __setattr__ override). Anyway,

 >>> class A(object):
 ...     class __metaclass__(type):
 ...         def TheAnswer(cls):
 ...             return "The Answer according to %s is 42" % cls.__name__
 ...         def __refuse(cls, v):
 ...             raise AttributeError, "Refusing to set %s.TheAnswer to %r"%(cls.__name__, v)
 ...         TheAnswer = property(TheAnswer, __refuse)
 ...
 ...
 >>> A.TheAnswer
 'The Answer according to A is 42'
 >>> A.TheAnswer = 123
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
   File "<stdin>", line 6, in __refuse
 AttributeError: Refusing to set A.TheAnswer to 123

Of course, access through an instance won't see this:

 >>> a=A()
 >>> a.TheAnswer
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 AttributeError: 'A' object has no attribute 'TheAnswer'

since TheAnswer is found in type(a)'s mro, but not type(A)'s:

 >>> type(a).mro()
 [<class '__main__.A'>, <type 'object'>]

 >>> type(A).mro()
 Traceback (most recent call last):
   File "<stdin>", line 1, in ?
 TypeError: descriptor 'mro' of 'type' object needs an argument

looks like you get type.mro as an unbound method that way...

 >>> type(A).mro(type(A))
 [<class '__main__.__metaclass__'>, <type 'type'>, <type 'object'>]

or

 >>> type.mro(A)
 [<class '__main__.A'>, <type 'object'>]
 >>> type.mro(type(A))
 [<class '__main__.__metaclass__'>, <type 'type'>, <type 'object'>]

or even

 >>> type.__dict__['mro']
 <method 'mro' of 'type' objects>
 >>> type.__dict__['mro'](A)
 [<class '__main__.A'>, <type 'object'>]
 >>> type.__dict__['mro'](type(A))
 [<class '__main__.__metaclass__'>, <type 'type'>, <type 'object'>]
 >>> type(A)
 <class '__main__.__metaclass__'>

Regards,
Bengt Richter



More information about the Python-list mailing list