Enrico a écrit :
Hi there,
I have the following situation (I tryed to minimize the code to concentrate
on the issue):

class A(object):
 def __getattr__(self, name):
  print 'A.__getattr__'
  if name == 'a': return 1
  raise AttributeError('%s not found in A' % name)

class B(object):
 def __getattr__(self, name):
  print 'B.__getattr__'
  if name == 'b': return 1
  raise AttributeError('%s not found in B' % name)

Both classes have a __getattr__ method.
Now I want to have a class that inherits from both so I write:

class C(B,A):
 pass

The problem arise when I try something like this:
c=C()
c.a
A.__getattr__
1
c.b
A.__getattr__

Traceback (most recent call last):
  File "<pyshell#47>", line 1, in <module>
    c.b
  File "<pyshell#42>", line 5, in __getattr__
    raise AttributeError('%s not found in A' % name)
AttributeError: b not found in A

That's what I would have expected.

I was expecting, after a fail in A.__getattr__,  a call to the __getattr__
method of B but it seems that after A.__getattr__ fails the exception stops
the flow.

Indeed. You explicitely raise, so the lookup stops here. You'd need to explicitely call on superclass instead to have B.__getattr__ called, ie:

class A(object):
    def __getattr__(self, name):
        if name == 'a':
            return 1
        return super(A, self).__getattr__(name)

class B(object):
    def __getattr__(self, name):
        if name == 'b':
            return 2
        return super(B, self).__getattr__(name)

class C(A, B):
    pass

c = C()
c.a
=> 1
c.b
=> 2

So, if I did understand well, B.__getattr__ will be never called
"automatically".  I don't know if this has a reason, if it is a design choice
or what else, any explanation is welcome.
>
Since A and B are not written by me I can only work on C.

Really ? You know, Python is a *very* dynamic language. If A and B are ordinary Python classes (ie: not builtin types, not C extensions, etc), you can monkeypatch them. But that's not necessarily the best thing to do (it would require more work than your actual solution).

The solution that
comes to my mind is to define a __getattr__ also in C and write something
like:

class C(A,B):
 def __getattr__(self, name):
  try:
   return A.__getattr__(self, name)
  except AttributeError:
   return B.__getattr__(self, name)

or more generically:

class C(A, B):
    def __getattr__(self, name):
        for cls in type(self).__mro__[1:]:
            try:
                return cls.__getattr__(self, name)
            except AttributeError:
                pass
        else:
            raise AttributeError('blah blah')

You could also override __getattribute__, but then again, this wouldn't be better (more code, and possible performances loss).

A better solution is welcome.

If you don't have commit rights on the module(s) defining A and B, then your solution is probably the simplest thing to do.

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to