Metaclasses vs. standard Python reflection?
Robin Becker
robin at jessikat.fsnet.co.uk
Sun May 4 05:17:48 EDT 2003
In article <wkWsa.38309$3M4.1072553 at news1.tin.it>, Alex Martelli <aleax at aleax.it>
writes
>Robin Becker wrote:
......
>
>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.
...... I regard the inner dynamic hook class as anonymous even though it has a name,
I think its exact name doesn't matter so it might as well be anonymous.
>> difficult without dynamic scopes to pass in the old __setattr__ if any.
>
>What are "dynamic scopes", pray?
local scopes, in my poor mind the old scoping was static and easily defined, now we
seem to have a dynamic global scope although it's really just as static.
......
>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'...?
...... well in the first place this doesn't work.
I first get a missing self in the __setattr__ so that needs to be changed to
def __setattr__(self,*args):
return self.__hook(*args+self.__oldhook)
and should that really be *(args+(self.__oldhook,)) ?
I also get errors because of the definition order. It's a bit dangerous to do inst.x
= when messing with __setattr__. I'm not sure how to rescue it as I start getting
some odd errors when I try and fix it further.
I rewrote it thusly and it seems to work
def hook_setattr2(obj, hook):
if not hasattr(obj, '_hooker__hook'):
class hooker(obj.__class__):
def __setattr__(self,k,v):
return self.__hook(self,k,v,self.__old_hook)
hooker.__name__ = obj.__class__.__name__
obj.__dict__['_hooker__old_hook'] = getattr(obj,'__setattr__',None)
obj.__dict__['_hooker__hook'] = hook
obj.__class__ = hooker
but it performs marginally slower than the other versions
>
>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.
>
>
this is fine in 2.2, but does it work for earlier versions?
def hook_setattr1(obj, hook):
if not hasattr(obj,'__proxy__setattr__'):
old_hook = getattr(obj,'__setattr__',None)
class hooker(obj.__class__):
__name__ = obj.__class__.__name__
__proxy_setattr__ = 1
def __setattr__(self,k,v):
return hook(self,k,v,old_hook)
obj.__class__ = hooker
.....
>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
>
my test code looks like
#####################
def hook_setattr0(obj,hook):
if not hasattr(obj,'__proxy__setattr__'):
C = obj.__class__
import new
obj.__class__=new.classobj(C.__name__,(C,)+C.__bases__,
{'__proxy__setattr__':1,
'__setattr__':lambda self,k,v,osa=getattr(obj,'__setattr__',None):
hook(self,k,v,osa)})
def hook_setattr1(obj, hook):
if not hasattr(obj,'__proxy__setattr__'):
old_hook = getattr(obj,'__setattr__',None)
class hooker(obj.__class__):
def __setattr__(self,k,v):
return hook(self,k,v,old_hook)
hooker.__name__ = obj.__class__.__name__
hooker.__proxy_setattr__ = 1
obj.__class__ = hooker
def hook_setattr2(obj, hook):
if not hasattr(obj, '_hooker__hook'):
class hooker(obj.__class__):
def __setattr__(self,k,v):
return self.__hook(self,k,v,self.__old_hook)
hooker.__name__ = obj.__class__.__name__
obj.__dict__['_hooker__old_hook'] = getattr(obj,'__setattr__',None)
obj.__dict__['_hooker__hook'] = hook
obj.__class__ = hooker
def hook(self,k,v,osa):
if k=='a':
pass #ignore
elif osa:
osa(k,v)
else:
self.__dict__[k] = v
class A:
def __init__(self):
self.__dict__['__internal__'] = {}
def __setattr__(self,k,v):
self.__internal__[k] = v
def run(n=1000000,hf=hook,shf=hook_setattr1):
from time import time
t0 = time()
a=A()
shf(a,hf)
for i in xrange(n):
setattr(a,'ab'[i%2],i)
t = time()-t0
print "n=%d hf=%s shf=%s total time=%.2f normalised time=%.6f" %
(n,hf,shf,t,t/n)
run(shf=hook_setattr0)
run(shf=hook_setattr1)
run(shf=hook_setattr2)
#####################
--
Robin Becker
More information about the Python-list
mailing list