473,320 Members | 1,904 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,320 software developers and data experts.

multiple inheritance and __getattr__

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.

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
Jul 28 '08 #1
4 3886
In article <48***********************@reader4.news.tin.it>,
"Enrico" <45**@755189.45wrote:
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
Jul 28 '08 #2
"Bruno Desthuilliers" <bd*****************@free.quelquepart.frha scritto
nel messaggio news:48***********************@news.free.fr...
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)
Hi Bruno,
this is an interisting point. Just to understand better: when I raise an
AttributeError the search stops but if I call a superclass (that for my
understanding raises an AttributeError) the search continues. At this point
I suspect that the search is doing something else, like checking if the
class is at the top of the hierarchy. Do you know where I can look for this,
probably in the core code of Python?
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).
I know that I can do whatIwant with class A and class B (monkeypatch!) but I
prefer to concentrate on my code and write a clean solution. Thanks for your
help.
Enrico
Jul 29 '08 #3
Le Monday 28 July 2008 16:48:09 Enrico, vous avez é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

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.
No getattr is a lookup fallback, classes which implement them in a
non-collaborative way are unlikely to be used for multiple inheritance.
Given how multiple inheritance work and __getattr__ semantic, I was surprised
it is not that easy to figure out how it could work in a collaborative, and
how far fromm this are common implementation of __getattr__.
Normally they should be implemented like that :
>>>[89]: class A(object) :
def __getattr__(self, name) :
if name == 'a' : return 'a'
try : g = super(A, self).__getattr__
except : raise AttributeError('no more __getattr__')
return g(name)
....:
....:
>>>[95]: class B(object) :
def __getattr__(self, name) :
if name == 'b' : return 'b'
try : g = super(B, self).__getattr__
except : raise AttributeError('no more __getattr__')
return g(name)
.....:
.....:
>>>[101]: class C(A, B) :
def __getattr__(self, name) :
if name == 'c' : return 'c'
try : g = super(C, self).__getattr__
except : raise AttributeError('no more __getattr__')
return g(name)
.....:
.....:
>>>[107]: C().a
...[107]: 'a'
>>>[108]: C().b
...[108]: 'b'
>>>[109]: C().c
...[109]: 'c'
>>>[110]: C().k
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)

/home/maric/<ipython consolein <module>()

/home/maric/<ipython consolein __getattr__(self, name)

/home/maric/<ipython consolein __getattr__(self, name)

/home/maric/<ipython consolein __getattr__(self, name)

AttributeError: no more __getattr__

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.
There is no way to repair those clases for mulitple inheritance except monkey
patching them.
The idea of this patch would be :

def collaborative_getattr(class_, old_name) :
old_one = getattr(class_, old_name)
def __getattr__(self, name) :
try : return old_one(self, name)
except AttributeError :
try : g = super(class_, self).__getattr__
except : raise AttributeError('no more __getattr__')
return g(name)

if not getattr(C, '_C_fixed__', False) :
C._C_fixed__ = C.__getattr__
C.__getattr__ = collaborative_getattr(C, '_C_fixed__')
That said, if your class C is a real facade for its ancestors A and B (A and B
won't appear at all in the hierarchies of your subclasses), your solution is
near the best one in terms of simplicity-efficiency. I said near the best one
because your __getattr__ isn't collaborative yet ! :).

--
_____________

Maric Michaud
Jul 29 '08 #4
In article <48***********************@news.free.fr>,
Bruno Desthuilliers <bd*****************@free.quelquepart.frwrote:
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.
??? Surely the reason the lookup stops there is that a __getattr__
was _found_. In the code below the lookup is not continuing,
there's a _second_ lookup started by the request for super.__getattr__.
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
--
David C. Ullrich
Jul 29 '08 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

5
by: Tobias Windeln | last post by:
Hi! I'm looking for suggestions on object-based inheritance in Python. Automatic forwarding (often called delegation) in Python is easy: def __getattr__(self, attr): return...
14
by: Axel Straschil | last post by:
Hello! Im working with new (object) classes and normaly call init of ther motherclass with callin super(...), workes fine. No, I've got a case with multiple inherance and want to ask if this...
2
by: Graham Banks | last post by:
Does using multiple inheritance introduce any more performance overhead than single inheritance?
5
by: Morgan Cheng | last post by:
It seems no pattern defined by GoF takes advantage of multiple inheritance. I am wondering if there is a situation where multiple inheritance is a necessary solution. When coding in C++, should...
22
by: Matthew Louden | last post by:
I want to know why C# doesnt support multiple inheritance? But why we can inherit multiple interfaces instead? I know this is the rule, but I dont understand why. Can anyone give me some concrete...
16
by: devicerandom | last post by:
Hi, I am currently using the Cmd module for a mixed cli+gui application. I am starting to refactor my code and it would be highly desirable if many commands could be built as simple plugins. ...
8
by: Anand | last post by:
I am trying to implement some kind of object inheritance. Just like one class can extend from another, I want to do the same on objects dynamically. I just thought that I can share my excitement...
7
by: snewman18 | last post by:
In learning about design patterns, I've seen discussion about using inheritance when an object's relationship to another object is 'is-a' and composition when the relationship is 'has-a'. Since...
7
by: barbaros | last post by:
Hello everybody, I am building a code for surface meshes (triangulations for instance). I need to implement Body objects (bodies can be points, segments, triangles and so on), then a Mesh will...
0
by: DolphinDB | last post by:
The formulas of 101 quantitative trading alphas used by WorldQuant were presented in the paper 101 Formulaic Alphas. However, some formulas are complex, leading to challenges in calculation. Take...
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
1
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: Vimpel783 | last post by:
Hello! Guys, I found this code on the Internet, but I need to modify it a little. It works well, the problem is this: Data is sent from only one cell, in this case B5, but it is necessary that data...
0
by: ArrayDB | last post by:
The error message I've encountered is; ERROR:root:Error generating model response: exception: access violation writing 0x0000000000005140, which seems to be indicative of an access violation...
0
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
by: af34tf | last post by:
Hi Guys, I have a domain whose name is BytesLimited.com, and I want to sell it. Does anyone know about platforms that allow me to list my domain in auction for free. Thank you
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.