multiple inheritance and __getattr__

David C. Ullrich dullrich at sprynet.com
Mon Jul 28 17:58:22 CEST 2008


In article <488ddc51$0$41654$4fafbaef at reader4.news.tin.it>,
 "Enrico" <4564 at 755189.45> wrote:

> 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
> 
> 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. 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.

Well, it's simply the way it works. When you say C(A, B) then A is
searched first for a given attribute and if A has no such attribute
then B is searched. If you really want to do something like this
then yes, you have to do something as below.

I suspect that the people who feel expert enough to talk about what
you should and shouldn't want are going to say that you shouldn't
want something like this - with things set up the way you have them
you really can't tell what's going to happen with c.__getattr__
unless you look at all the code in all the subclasses.

> Since A and B are not written by me I can only work on C. 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)
> 
> >>> c=C()
> >>> c.a
> A.__getattr__
> 1
> >>> c.b
> A.__getattr__
> B.__getattr__
> 1
> 
> A better solution is welcome.
> Many thanks, Enrico

-- 
David C. Ullrich



More information about the Python-list mailing list