[Python-bugs-list] [ python-Bugs-623669 ] __rdiv__ vs new-style classes
SourceForge.net
noreply@sourceforge.net
Mon, 06 Jan 2003 15:00:34 -0800
Bugs item #623669, was opened at 2002-10-15 14:00
You can respond by visiting:
https://sourceforge.net/tracker/?func=detail&atid=105470&aid=623669&group_id=5470
Category: Type/class unification
Group: Python 2.2.2
>Status: Closed
>Resolution: Fixed
Priority: 7
Submitted By: Tim Peters (tim_one)
Assigned to: Guido van Rossum (gvanrossum)
Summary: __rdiv__ vs new-style classes
Initial Comment:
In 2.2.2 and 2.3, consider this:
"""
class F:
def __init__(self):
print 'built an F'
def __div__(self, other):
print 'in F.__div__'
return F()
def __rdiv__(self, other):
print 'in F.__rdiv__'
return F() / self
class F2(F):
def __init__(self):
print 'built an F2'
3 / F2()
"""
This displays what I expect:
"""
built an F2
in F.__rdiv__
built an F
in F.__div__
built an F
"""
However, change F to derive from object:
class F(object):
and it's in infinite recursion, starting like so:
"""
built an F2
in F.__rdiv__
built an F
in F.__rdiv__
built an F
in F.__rdiv__
built an F
in F.__rdiv__
built an F
in F.__rdiv__
built an F
in F.__rdiv__
built an F
in F.__rdiv__
...
"""
Despite that F.__rdiv__ creates an explicit F() instance
and uses it on the LHS of /, F.__div__ is never invoked;
instead the RHS F2.__rdiv__ == F.__rdiv__ always gets
the nod.
Maybe this is intentional? I confess I've lost track of the
rules.
Changing the end of F.__rdiv__ to
return F().__div__(self)
brute-forces the desired outcome, so there is a
workaround.
----------------------------------------------------------------------
>Comment By: Guido van Rossum (gvanrossum)
Date: 2003-01-06 18:00
Message:
Logged In: YES
user_id=6380
OK, I've checked in a fix that checks whether other actually
overrides the __rdiv__ method (rather than ingeriting it
from self.__class__).
Sigh, this was hard work.
----------------------------------------------------------------------
Comment By: Guido van Rossum (gvanrossum)
Date: 2003-01-06 17:02
Message:
Logged In: YES
user_id=6380
I've got a dilemma.
The cause of the recursion is in line 3509 of typeobject.c,
in the SLOT1BINFULL() macro. The section starting with
if (do_other && \
(introduced by rev 2.82 of thatfile) tries to call the right
operand's __rdiv__ *before* the left operand's __div__ is
called in the specific case where both operands are
new-style classes that implement __div__ and __rdiv__.
This is intended to "do the right thing" in cases where D
derives from C and overrides some operation, and you call
C()/D(); without that code section, C.__div__ would be
invoked before D.__rdiv__, and since D() is a C instance,
C.__div__ would probably return a result -- just not the
result that D.__rdiv__ would have given.
The endless recursion reported above is caused by the fact
that D satisfies all the criteria, but D.__rdiv__ is really
just C.__rdiv__, which calls C()/D() recursively.
My dilemma is that this feature is not documented, and
classic classes don't work this way (C.__div__ would be
called). There are also no unit tests for the feature. So
ripping it out would be the quickest way to avoid the
recursion. OTOH, binary_op1() in abstract.c contains similar
code that catches a similar case where the base class C is a
built-in type (e.g. int). (Also undocumented and also
without unit tests.)
But checking that D's __rdiv__ is really the same as C's
__rdiv__ before calling it is fairly expensive and
convoluted (imagine the case where there's an intermediate
class C1, so that D derives from C1 derives from C, and C1
overrides C.__rdiv__). And even then one could construct
cases that would fool the test.
I'm going to think about this some more...
----------------------------------------------------------------------
You can respond by visiting:
https://sourceforge.net/tracker/?func=detail&atid=105470&aid=623669&group_id=5470