Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

__rdiv__ vs new-style classes #37325

Closed
tim-one opened this issue Oct 15, 2002 · 3 comments
Closed

__rdiv__ vs new-style classes #37325

tim-one opened this issue Oct 15, 2002 · 3 comments
Assignees
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs)

Comments

@tim-one
Copy link
Member

tim-one commented Oct 15, 2002

BPO 623669
Nosy @gvanrossum, @tim-one

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = 'https://github.com/gvanrossum'
closed_at = <Date 2003-01-06.23:00:34.000>
created_at = <Date 2002-10-15.18:00:04.000>
labels = ['interpreter-core']
title = '__rdiv__ vs new-style classes'
updated_at = <Date 2003-01-06.23:00:34.000>
user = 'https://github.com/tim-one'

bugs.python.org fields:

activity = <Date 2003-01-06.23:00:34.000>
actor = 'gvanrossum'
assignee = 'gvanrossum'
closed = True
closed_date = None
closer = None
components = ['Interpreter Core']
creation = <Date 2002-10-15.18:00:04.000>
creator = 'tim.peters'
dependencies = []
files = []
hgrepos = []
issue_num = 623669
keywords = []
message_count = 3.0
messages = ['12796', '12797', '12798']
nosy_count = 2.0
nosy_names = ['gvanrossum', 'tim.peters']
pr_nums = []
priority = 'high'
resolution = 'fixed'
stage = None
status = 'closed'
superseder = None
type = None
url = 'https://bugs.python.org/issue623669'
versions = ['Python 2.2']

@tim-one
Copy link
Member Author

tim-one commented Oct 15, 2002

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.

@tim-one tim-one closed this as completed Oct 15, 2002
@tim-one tim-one added the interpreter-core (Objects, Python, Grammar, and Parser dirs) label Oct 15, 2002
@tim-one tim-one closed this as completed Oct 15, 2002
@tim-one tim-one added the interpreter-core (Objects, Python, Grammar, and Parser dirs) label Oct 15, 2002
@gvanrossum
Copy link
Member

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...

@gvanrossum
Copy link
Member

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.

@ezio-melotti ezio-melotti transferred this issue from another repository Apr 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs)
Projects
None yet
Development

No branches or pull requests

2 participants