Question about __getattribute__/__getattr__

Alex Martelli aleax at aleax.it
Tue Oct 1 10:12:12 EDT 2002


<posted & mailed>

Joao Prado Maia wrote:
        ...
> My main concern right now is being able to get the full list of arguments
> passed to a method from inside __getattr__ :)

At the time __getattr__ is called, NO arguments are being passed yet,
nor is it yet known that just ONE "full list of arguments" will ever
be passed.  Consider:

class Any:
    def amethod(self, *args): print 'called with', args

class Wrapper:
    def __init__(self):
        self._boh = Any()
    def __getattr__(self, name):
        return getattr(self._boh, name)

x = Wrapper()

y = x.amethod

y(1,2,3)
y('peek','a','boo!')


When you look at a case where the method is fetched and called
at once, as in:

x.amethod(1,2,3)

there is no essential difference at all between this and the
case of:

y = x.amethod
y(1,2,3)

Therefore, it should be clear that at the instant "x.amethod"
gets fetched, which is when __getattr__ is running, there IS
no "full list of arguments" -- yet.  There may never be one,
there may be several, or indeed there may be exactly one, but
any of these eventualities are FUTURE prospects.

Therefore, if you want to control such aspects, you cannot
return the "bare" callable-attribute (method) as obtained
by e.g. the getattr call above; you must WRAP the callable
into another, controlling callable.  For example:

class CallableWrapper:
    def __init__(self, name, thecallable):
        self.name = name
        self.thecallable = thecallable
    def __call__(self, *args, **kwds):
        print '%s called' % self.name,
        if args: print 'with positional args %s, and' % args,
        else: print 'with no positional args and',
        if kwds: print 'named args %s' % kwds
        else: print 'no named args'
        return self.thecallable(*args, **kwds)

class WrapperPlus:
    def __init__(self):
        self._boh = Any()
    def __getattr__(self, name):
        result = getattr(self._boh, name)
        if callable(result):
            result = CallableWrapper(name, result)
        return result

Now this will work as intended in all of the above cases.


Alex




More information about the Python-list mailing list