Pb with descriptor and super ...

E

Erwan Adam

Hi all,

I have a problem with the use of descriptors
and super. The file is :

# ---------------

class Desc(object):
def __init__(self, class_name):
self.class_name = class_name
return
def __get__(self, obj, typ):
print "Desc.__get__ : class_name is %s, obj is %s and typ is
%s"%(self.class_name, obj, typ)
return
pass

class A(object):
attr = Desc("A")
pass

class B(A):
attr = Desc("B")
pass

b = B()

print "-------"
print "Getting b.attr ..."
print "I hope that : class_name is B, obj is b and typ is B"
b.attr
print "-------"
print "Getting super(B, b).attr ..."
print "I hope that : class_name is A, obj is b and typ is A but it is
not the case !"
super(B, b).attr
print "-------"

# -----------

and the result is :

-------
Getting b.attr ...
I hope that : class_name is B, obj is b and typ is B
Desc.__get__ : class_name is B, obj is <__main__.B object at 0xb7b1f8ec>
and typ is <class '__main__.B'>
-------
Getting super(B, b).attr ...
I hope that : class_name is A, obj is b and typ is A but it is not the
case !
Desc.__get__ : class_name is A, obj is <__main__.B object at 0xb7b1f8ec>
and typ is <class '__main__.B'>
-------

I expected that when getting super(B, b).attr, typ is A, but it is not
the case ...

python used :

[adam@is111902 /home/adam/Work/Python]> python
Python 2.4.3 (#2, Sep 18 2006, 21:07:35)
[GCC 4.1.1 20060724 (prerelease) (4.1.1-3mdk)] on linux2
Type "help", "copyright", "credits" or "license" for more information.

Best regards,


E.A.
 
E

Erwan Adam

Erwan Adam a écrit :
Hi all,

I have a problem with the use of descriptors
and super. The file is :

# ---------------

class Desc(object):
def __init__(self, class_name):
self.class_name = class_name
return
def __get__(self, obj, typ):
print "Desc.__get__ : class_name is %s, obj is %s and typ is
%s"%(self.class_name, obj, typ)
return
pass

class A(object):
attr = Desc("A")
pass

class B(A):
attr = Desc("B")
pass

b = B()

print "-------"
print "Getting b.attr ..."
print "I hope that : class_name is B, obj is b and typ is B"
b.attr
print "-------"
print "Getting super(B, b).attr ..."
print "I hope that : class_name is A, obj is b and typ is A but it is
not the case !"
super(B, b).attr
print "-------"

# -----------

and the result is :

-------
Getting b.attr ...
I hope that : class_name is B, obj is b and typ is B
Desc.__get__ : class_name is B, obj is <__main__.B object at 0xb7b1f8ec>
and typ is <class '__main__.B'>
-------
Getting super(B, b).attr ...
I hope that : class_name is A, obj is b and typ is A but it is not the
case !
Desc.__get__ : class_name is A, obj is <__main__.B object at 0xb7b1f8ec>
and typ is <class '__main__.B'>

Moreover, on the page :

http://users.rcn.com/python/download/Descriptor.htm

the author writes :

"The call super(B, obj).m() searches obj.__class__.__mro__ for the base
class A immediately following B and then returns
A.__dict__['m'].__get__(obj, A)."

which it seems not the case ...

Is it a bug or a mis-understood ?

Regards,

E.A.

python used :

[adam@is111902 /home/adam/Work/Python]> python
Python 2.4.3 (#2, Sep 18 2006, 21:07:35)
[GCC 4.1.1 20060724 (prerelease) (4.1.1-3mdk)] on linux2
Type "help", "copyright", "credits" or "license" for more information.

Best regards,


E.A.
 
E

Erwan Adam

Gabriel Genellina a écrit :
Descriptors for "super" behave a bit different than for classes. See
http://www.python.org/2.2/descrintro.html#cooperation

--Gabriel Genellina

Hi Gabriel,

Thanks for your answer ... I followed the link
and tried to use the "fully functional implementation of
the super() built-in class in pure Python" given
on this page and the result is quite surprising :

# -------------

class Super(object):
def __init__(self, type, obj=None):
self.__type__ = type
self.__obj__ = obj
def __get__(self, obj, type=None):
if self.__obj__ is None and obj is not None:
return Super(self.__type__, obj)
else:
return self
def __getattr__(self, attr):
if isinstance(self.__obj__, self.__type__):
starttype = self.__obj__.__class__
else:
starttype = self.__obj__
mro = iter(starttype.__mro__)
for cls in mro:
if cls is self.__type__:
break
# Note: mro is an iterator, so the second loop
# picks up where the first one left off!
for cls in mro:
if attr in cls.__dict__:
x = cls.__dict__[attr]
if hasattr(x, "__get__"):
x = x.__get__(self.__obj__)
return x
raise AttributeError, attr

class Desc(object):
def __init__(self, class_name):
self.class_name = class_name
return
def __get__(self, obj, typ=None):
print "Desc.__get__ : class_name is %s, obj is %s and typ is
%s"%(self.class_name, obj, typ)
return
pass

class A(object):
attr = Desc("A")
pass

class B(A):
attr = Desc("B")
pass

b = B()

print "Using built-in super"
attr = super(B, b).attr
print "Using python Super"
attr = Super(B, b).attr

class MySuper(object):
def __init__(self, type, obj=None):
self.__type__ = type
self.__obj__ = obj
def __get__(self, obj, type=None):
if self.__obj__ is None and obj is not None:
return Super(self.__type__, obj)
else:
return self
def __getattr__(self, attr):
if isinstance(self.__obj__, self.__type__):
starttype = self.__obj__.__class__
else:
starttype = self.__obj__
mro = iter(starttype.__mro__)
for cls in mro:
if cls is self.__type__:
break
# Note: mro is an iterator, so the second loop
# picks up where the first one left off!
for cls in mro:
if attr in cls.__dict__:
x = cls.__dict__[attr]
if hasattr(x, "__get__"):
x = x.__get__(self.__obj__, cls)
return x
raise AttributeError, attr

print "Using python MySuper"
attr = MySuper(B, b).attr


# ------------------------------

it gives :

[adam@is111902 /home/adam/Work/Python]> python super_from_guido.py
Using built-in super
Desc.__get__ : class_name is A, obj is <__main__.B object at 0xb7bc302c>
and typ is <class '__main__.B'>
Using python Super
Desc.__get__ : class_name is A, obj is <__main__.B object at 0xb7bc302c>
and typ is None
Using python MySuper
Desc.__get__ : class_name is A, obj is <__main__.B object at 0xb7bc302c>
and typ is <class '__main__.A'>

the Super gives None for typ ... which is quite surprising !
In MySuper, I just change

x = x.__get__(self.__obj__)

by

x = x.__get__(self.__obj__, cls)

at the -3 :) line of the class ... and it gives the result I expected
at the beginning : obj is b and typ is A !!!!


Best regards,

E.A.
 
G

Gabriel Genellina

Thanks for your answer ... I followed the link
and tried to use the "fully functional implementation of
the super() built-in class in pure Python" given
on this page and the result is quite surprising :

It appears that there is some disagreement on how the super object should
behave.
If you want to know what it actually *does* now (not what it *should* do)
you can look at typeobject.c, but it's a bit hard to read, and I don't
understand it in full...
Maybe looking at the test suite is easier, in test_descr.py
 
A

Arnaud Delobelle

[adam@is111902 /home/adam/Work/Python]> python super_from_guido.py
Using built-in super
Desc.__get__ : class_name is A, obj is <__main__.B object at 0xb7bc302c>
and typ is <class '__main__.B'>
Using python Super
Desc.__get__ : class_name is A, obj is <__main__.B object at 0xb7bc302c>
and typ is None
Using python MySuper
Desc.__get__ : class_name is A, obj is <__main__.B object at 0xb7bc302c>
and typ is <class '__main__.A'>

It seems to me that it is the built-in super which has the correct
behaviour. When you write obj.attr, python calls attr.__get__(obj,
type(obj)) and not attr.__get__(obj) which sets the second arg to
None. You can check this by creating a barebone descriptor that simply
prints its arguments:

class D(object):
def __get__(self, obj, objtype):
print obj, objtype

class A(object):
d=D()

A().d
<__main__.A object at 0x133c5f0> <class '__main__.A'> # objtype is not
None!

Therefore in your case when you do super(B, b).attr then
A.__dict__['attr'].__get__(b, type(b)) is called. You can try this, it
should give the same output as builtin super.
the Super gives None for typ ... which is quite surprising !
In MySuper, I just change

x = x.__get__(self.__obj__)

by

x = x.__get__(self.__obj__, cls)
at the -3 :) line of the class ... and it gives the result I expected
at the beginning : obj is b and typ is A !!!!

Neither of these are right I think. If my explanation above is right
the correct line should be:
x = x.__get__(self.__obj__, type(self.__obj__))

HTH
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,981
Messages
2,570,188
Members
46,731
Latest member
MarcyGipso

Latest Threads

Top