Read-only class properties
Bengt Richter
bokr at oz.net
Sun Jul 10 21:20:06 EDT 2005
On 10 Jul 2005 13:38:22 -0700, "George Sakkis" <gsakkis at rutgers.edu> wrote:
>I'm trying to write a decorator similar to property, with the
>difference that it applies to the defining class (and its subclasses)
>instead of its instances. This would provide, among others, a way to
>define the equivalent of class-level constants:
>
>class Foo(object):
> @classproperty
> def TheAnswer(cls):
> return "The Answer according to %s is 42" % cls.__name__
>
>>>> Foo.TheAnswer
>The Answer according to Foo is 42
>>> Foo.TheAnswer = 0
>exceptions.AttributeError
>...
>AttributeError: can't set class attribute
>
>I read the 'How-To Guide for Descriptors'
>(http://users.rcn.com/python/download/Descriptor.htm) that describes
>the equivalent python implementation of property() and classmethod()
>and I came up with this:
>
>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()
>
>Accessing Foo.TheAnswer works as expected, however __set__ is
>apparently not called because no exception is thrown when setting
>Foo.TheAnswer. What am I missing here ?
>
I suspect type(Foo).TheAnswer is not found and therefore TheAnswer.__set__ is not
looked for, and therefore it becomes an ordinary attribute setting. I suspect this
is implemented in object.__setattr__ or type.__setattr__ as the case may be, when
they are inherited. So I introduced a __setattr__ in type(Foo) by giving Foo
a metaclass as its type(Foo). First I checked whether the attribute type name was
'Descriptor' (not very general ;-) and raised an attribute error if so.
Then I made a class Bar version of Foo and checked for __set__ and called that
as if a property of type(Bar) instances. See below.
----< classprop.py >----------------------------------------------------
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 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__
class Bar(object):
class __metaclass__(type):
def __setattr__(cls, name, value):
attr = cls.__dict__.get(name)
if hasattr(attr, '__set__'):
attr.__set__(cls, value) # like an instance attr setting
else:
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__
if __name__ == '__main__':
print Foo.TheAnswer
Foo.notTheAnswer = 'ok'
print 'Foo.notTheAnswer is %r' % Foo.notTheAnswer
print Foo.AnotherAnswer
try: Foo.TheAnswer = 123
except AttributeError, e: print '%s: %s' %(e.__class__.__name__, e)
try: Foo.AnotherAnswer = 456
except AttributeError, e: print '%s: %s' %(e.__class__.__name__, e)
print Bar.TheAnswer
Bar.notTheAnswer = 'ok'
print 'Bar.notTheAnswer is %r' % Bar.notTheAnswer
print Bar.AnotherAnswer
try: Bar.TheAnswer = 123
except AttributeError, e: print '%s: %s' %(e.__class__.__name__, e)
try: Bar.AnotherAnswer = 456
except AttributeError, e: print '%s: %s' %(e.__class__.__name__, e)
------------------------------------------------------------------------
Result:
[18:17] C:\pywk\clp>py24 classprop.py
The Answer according to Foo is 42
Foo.notTheAnswer is 'ok'
Another Answer according to Foo is 43
AttributeError: setting Foo.TheAnswer to 123 is not allowed
AttributeError: setting Foo.AnotherAnswer to 456 is not allowed
The Answer according to Bar is 42
Bar.notTheAnswer is 'ok'
Another Answer according to Bar is 43
AttributeError: can't set class attribute
AttributeError: can't set class attribute
Regards,
Bengt Richter
More information about the Python-list
mailing list