On Sun, 10 Jul 2005 21:10:36 -0700, Michael Spencer <[EMAIL PROTECTED]> 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 -- http://mail.python.org/mailman/listinfo/python-list