Path: csiph.com!usenet.pasdenom.info!aioe.org!.POSTED!not-for-mail From: Marco Buttu Newsgroups: comp.lang.python Subject: Re: Metaclass/abc hackery Date: Sat, 12 Oct 2013 06:56:12 +0200 Organization: Aioe.org NNTP Server Lines: 95 Message-ID: <5258D66C.8040404@gmail.com> References: NNTP-Posting-Host: m4UkYrtsNuTw1PNQL3+V/A.user.speranza.aioe.org Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit X-Complaints-To: abuse@aioe.org User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:16.0) Gecko/20121010 Thunderbird/16.0.1 X-Notice: Filtered by postfilter v. 0.8.2 Xref: csiph.com comp.lang.python:56724 On 10/12/2013 04:47 AM, Demian Brecht wrote: > > Working on this though brought up a question: Is there anything in the > data model that acts like "__setattr__" but when operating on a class > definition instead of an instance? I'd be able to get rid of the > late_bind function if something like that's available... Not likely > something that would be used very often, but would likely sometimes be > useful. > > Thanks, I am not sure about your question, but I try to explain a bit some possibilities. If you define a __setattr__ method in the metaclass, then you can intercept the attribute assignments only after class creation: >>> class MetaFoo(type): ... def __setattr__(cls, name, value): ... print("in __setattr__(): ", name, value) ... super().__setattr__(name, value) ... >>> >>> class Foo(metaclass=MetaFoo): ... a = 33 As you can see, the above code does not print the message. But after class creation it does: >>> Foo.a = 33 in __setattr__(): a 33 This because during the class creation there is any class yet, so it is not possible to intercept argument assignment by __setattr__. If you want to intercept the assignments during class creation too, you can intercept the class attribute dictionary assignment. In this case you can just write a dictionary object that overrides __setitem__, and then by overriding the __prepare__ metaclass method in order to return this dictionary: >>> class Namespace(dict): ... def __setitem__(self, name, value): ... print('In Namespace.__setitem__():', name, value) ... super().__setitem__(name, value) ... >>> class MetaFoo(type): ... def __prepare__(clsname, bases): ... return Namespace() ... def __setattr__(cls, name, value): ... print("In MetaFoo.__setattr__(): ", name, value) ... super().__setattr__(name, value) ... >>> class Foo(metaclass=MetaFoo): ... a = 33 ... In Namespace.__setitem__(): __module__ __main__ In Namespace.__setitem__(): __qualname__ Foo In Namespace.__setitem__(): a 33 >>> Foo.a = 33 In MetaFoo.__setattr__(): a 33 Of course, it is not a so good solution, because if you need to manage in the same way either the attributes before or after the class creation, you have to do it by writing some code outside the methods: >>> def manage(name, value): ... print('do something with', name, value) ... >>> class Namespace(dict): ... def __setitem__(self, name, value): ... print('In Namespace.__setitem__():', name, value) ... manage(name, value) ... super().__setitem__(name, value) ... >>> class MetaFoo(type): ... def __prepare__(clsname, bases): ... return Namespace() ... def __setattr__(cls, name, value): ... print("In MetaFoo.__setattr__(): ", name, value) ... manage(name, value) ... super().__setattr__(name, value) ... >>> class Foo(metaclass=MetaFoo): ... a = 33 ... In Namespace.__setitem__(): __module__ __main__ do something with __module__ __main__ In Namespace.__setitem__(): __qualname__ Foo do something with __qualname__ Foo In Namespace.__setitem__(): a 33 do something with a 33 >>> Foo.a = 33 In MetaFoo.__setattr__(): a 33 do something with a 33 -- Marco Buttu