Re: multiple inheritance and __getattr__

2008-07-29 Thread Enrico
Bruno Desthuilliers [EMAIL PROTECTED] ha scritto
nel messaggio news:[EMAIL PROTECTED]
 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


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


Re: multiple inheritance and __getattr__

2008-07-29 Thread Maric Michaud
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
---
AttributeErrorTraceback (most recent call last)

/home/maric/ipython console in module()

/home/maric/ipython console in __getattr__(self, name)

/home/maric/ipython console in __getattr__(self, name)

/home/maric/ipython console in __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
--
http://mail.python.org/mailman/listinfo/python-list


Re: multiple inheritance and __getattr__

2008-07-29 Thread David C. Ullrich
In article [EMAIL PROTECTED],
 Bruno Desthuilliers [EMAIL PROTECTED] wrote:

 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
--
http://mail.python.org/mailman/listinfo/python-list

multiple inheritance and __getattr__

2008-07-28 Thread Enrico
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


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


Re: multiple inheritance and __getattr__

2008-07-28 Thread David C. Ullrich
In article [EMAIL PROTECTED],
 Enrico [EMAIL PROTECTED] 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
--
http://mail.python.org/mailman/listinfo/python-list


Re: multiple inheritance and __getattr__

2008-07-28 Thread Bruno Desthuilliers

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