On Aug 12, 9:14 pm, Steven Bethard <[EMAIL PROTECTED]> wrote: > Michele Simionato wrote: > > On Aug 10, 7:09 pm, Steven Bethard <[EMAIL PROTECTED]> wrote: > >> There were also a few recipes posted during this discussion that wrap > >> weakrefs up a bit nicer so it's easier to use them in place of __del__: > > >>http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/519635 > > > I knew about your recipe and I thought it was clever, *very* > > clever. I have been thinking about it a bit more today and > > here is an alternative, which does not require metaclasses, > > does not require descriptors, and does not require the user to > > declare the attributes she will use in the __finalize__ method. > > Warning: it is no more tested than the test case in your recipe: > > > import weakref > > > _get = object.__getattribute__ > > _set = object.__setattr__ > > _del = object.__delattr__ > > > def getinnerobj(self): > > return _get(self, '*innerobj*') > > > class Impostor(object): > > "It tries very hard to impersonate the inner object" > > def __init__(self, obj): > > _set(self, '*innerobj*', obj) > > def __getattribute__(self, name): > > return getattr(getinnerobj(self), name) > > def __setattr__(self, name, value): > > _set(getinnerobj(self), name, value) > > def __delattr__(self, name): > > _del(getinnerobj(self), name) > > > _refset = set() > > > class Finalized(object): > > def __new__(cls, *args, **kw): > > self = super(Finalized, cls).__new__(cls, *args, **kw) > > self.__init__(*args, **kw) > > def finalize(ref, refset=_refset): > > refset.remove(ref) > > cls.__finalize__(self) > > fake = Impostor(self) > > _refset.add(weakref.ref(fake, finalize)) > > return fake > > def __finalize__(self): > > pass > > The problem here is that __getattribute__ doesn't work for things like > __getitem__.
Yeah, it is the old issue of special methods being special, i.e. len(x) is not exactly x.__len__(). I am not sure if this can be changed in Py3k, it is certainly annoying in some (rare) circumstances, as here. One workaround is to add all special methods to the Impostor class: SPECIALMETHODS = ['__%s__' % name for name in ''' abs add and call concat contains delitem delslice div eq floordiv ge getitem getslice gt iadd iand iconcat idiv ifloordiv ilshift imod imul index inv invert ior ipow irepeat irshift isub iter itruediv ixor le len lshift lt mod mul ne neg not or pos pow repeat repr rshift setitem setslice str sub truediv xor '''.split()] def add_special_method(cls, name): def meth(self, *args): return getattr(self, name) (*args) meth.__name__ = name setattr(cls, name, meth) for name in SPECIALMETHODS: add_special_method(Impostor, name) In this way the Impostor can emulate even the special methods. Here is an example: >>> class C(object): ... pass ... >>> c=Impostor(C()) >>> print c <__main__.C object at 0x102a390> Notice that the impostor is calling the __str__ method of C, so it really looks like a C object. Moreover >>> c.__class__ <class '__main__.C'> and >>> isinstance(c, C) True so the Impostor is doing a damn pretty good job of imposture for C objects. Of course it does what it can, and it cannot impersonate C objects completely: >>> type(c) <class '__main__.Impostor'> so code using checks like ``type(c) is C`` would break and the approach here cannot be considered more than a hack. Still a cool hack, I would say ;) Michele Simionato -- http://mail.python.org/mailman/listinfo/python-list