Late initialization using __getattribute__

bukzor workitharder at gmail.com
Thu Sep 4 12:36:23 EDT 2008


> >>> so unfortunately I think I need to use __getattribute__
> >>> to do this. I'm doing all this just to make the connection not
> >>> actually connect until used.
> >> I may be dumb, but I don't get how this is supposed to solve your
> >> problem. But anyway : there's a known design pattern for what you're
> >> trying to do, that doesn't require mixins nor messing with
> >> __getattribute__ (which, I repeat, is more often than not something you
> >> *don't* want to do). The name of the design pattern is "proxy". I
> >> strongly suggest that you 1/ try to cure the real problem instead of
> >> hacking around and 2/ read about the proxy design pattern.
>
> >> My 2 cents...
>
> > I like the idea of mix-ins, but can't figure out how to make a proxy
> > work that way.
>
> You mean, "how to use a proxy for lazy initialization" ? Heck, that's
> the exact use case in the GoF.

I mean, "how to make a MixIn class that uses the proxy pattern". I'd
like to be able to do something like this:

class SuperCursor(FeatureOneMixIn, FeatureTwoMixin, ...,
VanillaCursor): pass

This works with my current implementation. After thinking about it
more, I think I've got such a thing written. I had to use
inspect.getmro and new.classobj to do it, but it works and it honors
the usual mro (as far as I can tell). I've put the code at the bottom
to (try to) maintain readability.


> > For a long time I had a proxy class that added five or
> > six features on top of the MySQLdb package, but it wasn't configurable
> > enough, and I'm working on splitting each feature into its own MixIn
> > class.
>
> > As an aside, this is working for me pretty well. The "reconnect"
> > method (inheritied from a "Reconnectable" mixin) uses several of the
> > object's attributes, so I need to set _inited beforehand so that I
> > don't get into an infinite __getattribute__ loop. What I'd *really*
> > like to do is remove __getattribute__ from the object at that point.
>
> You can't. Or, more exactly, all you can do is remove __getattribute__
> from the mixin class - but then the mixin class won't work anymore. I
> don't mean to be condescendant, but it looks like you don't have a clear
> understanding of Python's object model here - else you wouldn't even
> consider doing such a thing. FWIW, I posted a solution based on the
> __getattr__ hook, which did work - at least for the "specs" implied by
> your code snippet.

My toy example turned out to be not the best representation of the
problem.
The base class has attributes that "exist" but either throw errors or
segfault
if used before reconnect() is called. This means that I need to
capture more than
just the attributes that would throw AttributeError.




#CODE########################################
class Base(object):
    def __init__(self, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs
    def __str__(self): return "<Base object created with %s %s>" %
(self.args, self.kwargs)

class MixIn2(object):
    def __str__(self):
        return "<MixIn2 with %s>" % super(MixIn2, self).__str__()

class MixIn1(object):
    def __str__(self):
        return "<MixIn1 with %s>" % super(MixIn1, self).__str__()

class ProxyMixIn(object):
    def __init__(self, *args, **kwargs):
        self.__proxied = None
        self.__args = args
        self.__kwargs = kwargs
    def __getattr__(self, attr):
        print "Getting", attr
        try: return getattr(self.__proxied, attr)
        except AttributeError:
            if self.__proxied: raise
            else:
                from inspect import getmro
                mro = getmro(self.__class__)
                mro = mro[list(mro).index(ProxyMixIn) + 1:]
                print "Proxied mro", mro
                from new import classobj
                self.__proxied = classobj("Proxied", mro, globals())
(*self.__args, **self.__kwargs)
                return getattr(self.__proxied, attr)
    def __str__(self):
        return "<Proxy of %s>" % super(ProxyMixIn, self).__str__()

class Proxy(MixIn1, ProxyMixIn, MixIn2, Base): pass

def main():
    p = Proxy(1,2,3, one=1, two=2)
    print p
main()

#OUTPUT######################################
Getting args
Proxied mro (<class '__main__.MixIn2'>, <class '__main__.Base'>, <type
'object'>)
Getting kwargs
<MixIn1 with <Proxy of <MixIn2 with <Base object created with (1, 2,
3) {'two': 2, 'one': 1}>>>>




More information about the Python-list mailing list