On 10 Jul 2005 13:38:22 -0700, "George Sakkis" <gs*****@rutger s.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.TheAnswerThe Answer according to Foo is 42 Foo.TheAnswer = 0
exceptions.Att ributeError
...
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(f unction):
class Descriptor(obje ct):
def __get__(self, obj, objtype):
return function(objtyp e)
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.TheAnswe r. What am I missing here ?
I suspect type(Foo).TheAn swer 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.__setatt r__ 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(f unction):
class Descriptor(obje ct):
def __get__(self, obj, objtype):
return function(objtyp e)
def __set__(self, obj, value):
raise AttributeError, "can't set class attribute"
return Descriptor()
class Foo(object):
class __metaclass__(t ype):
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(c ls):
return "Another Answer according to %s is 43" % cls.__name__
class Bar(object):
class __metaclass__(t ype):
def __setattr__(cls , name, value):
attr = cls.__dict__.ge t(name)
if hasattr(attr, '__set__'):
attr.__set__(cl s, 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(c ls):
return "Another Answer according to %s is 43" % cls.__name__
if __name__ == '__main__':
print Foo.TheAnswer
Foo.notTheAnswe r = 'ok'
print 'Foo.notTheAnsw er is %r' % Foo.notTheAnswe r
print Foo.AnotherAnsw er
try: Foo.TheAnswer = 123
except AttributeError, e: print '%s: %s' %(e.__class__._ _name__, e)
try: Foo.AnotherAnsw er = 456
except AttributeError, e: print '%s: %s' %(e.__class__._ _name__, e)
print Bar.TheAnswer
Bar.notTheAnswe r = 'ok'
print 'Bar.notTheAnsw er is %r' % Bar.notTheAnswe r
print Bar.AnotherAnsw er
try: Bar.TheAnswer = 123
except AttributeError, e: print '%s: %s' %(e.__class__._ _name__, e)
try: Bar.AnotherAnsw er = 456
except AttributeError, e: print '%s: %s' %(e.__class__._ _name__, e)
------------------------------------------------------------------------
Result:
[18:17] C:\pywk\clp>py2 4 classprop.py
The Answer according to Foo is 42
Foo.notTheAnswe r is 'ok'
Another Answer according to Foo is 43
AttributeError: setting Foo.TheAnswer to 123 is not allowed
AttributeError: setting Foo.AnotherAnsw er to 456 is not allowed
The Answer according to Bar is 42
Bar.notTheAnswe r is 'ok'
Another Answer according to Bar is 43
AttributeError: can't set class attribute
AttributeError: can't set class attribute
Regards,
Bengt Richter