Metaclasses vs. standard Python reflection?

Alex Martelli aleax at aleax.it
Sat May 3 17:16:12 EDT 2003


Robin Becker wrote:
   ...
>>def hook_setattr(inst):
>>    class hooker(inst.__class__):
>>        def __setattr__(*args):
>>            print 'hook(%r,%r,%r)' % args
>>    hooker.__name__ = inst.__class__.__name__
>>    inst.__class__ = hooker
   ...
>>What exactly IS the problem you see with the hook_setattr approach?
   ...
> The anonymous class idea is great, but it's I find it

What "anonymous class" are you taking about...?  I don't think there
is any such thing -- and surely there isn't one in my code snippet
up there in function hook_setattr.

> difficult without dynamic scopes to pass in the old __setattr__ if any.

What are "dynamic scopes", pray?

> I want to use something like
> 
> def _addHook(obj,hook):
>         if not hasattr(obj,'__proxy__setattr__'):
>                 C = obj.__class__
>                 D={}
>                 D['__proxy__setattr__']=1
>                 D['__setattr__'] = lambda self,k,v,osa=getattr(obj,\
>                    '__setattr__',None),hook=hook: hook(self,k,v,osa)
>                 import new
>                 obj.__class__=new.classobj(C.__name__,(C,)+C.__bases__,D)
> 
> where the hook function takes a 4'th argument representing a possible
> existing __setattr__.

If you insist on doing it the complicated way, with lambda and
module new, I guess you can.  I just don't see what advantage this
is supposed to provide with respect to doing it the simple way.
Me, I'd still do it the simple way -- maybe because I still do not
understand the problem...?

E.g., if you don't want function hook_setattr to do any work on
objects which have an attribute named __proxy_setattr__, just insert
in my above-quoted function hook_setattr the first statement:
    if hasattr(inst, '__proxy_setattr__'): return

If you want the class of the resulting object to have a class
attribute named __proxy_setattr__ and worth 1, just add it in
the body of class hooker.  And similarly, if you want the setattr
function to do potentially fancy stuff based on object state,
well then, set and use that object state.  E.g.:

def hook_setattr(inst, hook):
    if hasattr(inst, '__proxy_setattr__'): return
    class hooker(inst.__class__):
        __proxy_setattr__ = 1
        def __setattr__(*args):
            return self.__hook(*args+self.__oldhook)
    hooker.__name__ = inst.__class__.__name__
    inst._hooker_hook = hook
    inst._hooker_oldhook = getattr(inst,'__setattr__',None),
    inst.__class__ = hooker

How is this inferior to that intricate 'lambda'...?


> Perhaps I misunderstand class scopes and you can do this better in 1.5.2.

Uh, what's 1.5.2 gotta do with it...?  I very, VERY much doubt you'd
want to use custom metaclasses in 1.5.2.  It's the innovation in 2.2
that makes them usable.  I'm not sure what class scopes have to do
with anything here; the function above should work in 1.5.2 all the
way to 2.3 (untested, and I have no old release installed to test
with, but any problem should be very minor anyway).


> I keep thinking it should be possible to do
> 
> class _HOOK(C):
>         __proxy__setattr__ = 1
> 
> _HOOK.__setattr__ = lambda.......hook(self,k,v,osa)
> _HOOK.__name__ = C.__name__

Sure.  That's not very different from what I'm doing in the
latest version of hook_setattr, except that I avoid the weird
approach of using a lambda to store state, when state is so
much easier to keep in object attributes and def is such a
simpler and smoother way to create functions than lambda.


> but am unsure how this plays in new style/old style etc.

There should be no problem with my function hook_setattr, no
matter whether inst belongs to a new-style or old-style
class.  Even if that class has __slots__, no problem -- the
hooker class doesn't, so inst now has a dict, whether it had
one previously or not, so we can safely store the hook and
the old bound-method for __setattr__ if any.


Alex





More information about the Python-list mailing list