Issue623669
This issue tracker has been migrated to GitHub,
and is currently read-only.
For more information,
see the GitHub FAQs in the Python's Developer Guide.
Created on 2002-10-15 18:00 by tim.peters, last changed 2022-04-10 16:05 by admin. This issue is now closed.
Messages (3) | |||
---|---|---|---|
msg12796 - (view) | Author: Tim Peters (tim.peters) * | Date: 2002-10-15 18:00 | |
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. |
|||
msg12797 - (view) | Author: Guido van Rossum (gvanrossum) * | Date: 2003-01-06 22:02 | |
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... |
|||
msg12798 - (view) | Author: Guido van Rossum (gvanrossum) * | Date: 2003-01-06 23:00 | |
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. |
History | |||
---|---|---|---|
Date | User | Action | Args |
2022-04-10 16:05:45 | admin | set | github: 37325 |
2002-10-15 18:00:04 | tim.peters | create |