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