[Python-Dev] Bug in PyNumber_InPlacePower implementation?

Guido van Rossum guido@python.org
Wed, 29 May 2002 11:32:51 -0400

> > This is exactly the same as what binary_op1() does.  I added this
> > twist intentionally because of a use case where a subclass of a
> > numeric type wants to override a binary (or ternary) operator defined
> > by the base class.
> I don't think you understand what I mean. I'm talking
> about *in-place* operations.

You're right, I hadn't realized that this was about inplace.

> For in-place binary operations we have, e.g.
> INPLACE_BINOP(PyNumber_InPlaceSubtract, nb_inplace_subtract, nb_subtract, "-=")
>                                         ^^^^^^^^^^^^^^^^^^^  ^^^^^^^^^^^
> Both the in-place and non-in-place slots are passed to
> binary_iop, which uses the in-place slot for the first 
> operand and the non-in-place slot for the second.
> But ternary_op only gets *one* slot passed to it, which it
> uses for everything. When doing an in-place ternary op,
> this is the in-place slot. What this seems to mean is
> that, if we have isinstance(a, A) and isinstance(b, B)
> and issubclass(B, A), then
>   a **= b
> has the potential to in-place-modify b instead of a!

Not quite -- the arguments won't be reversed, but it may call B's
inplace power function with an A instance as the first argument.  This
can return NotImplemented if it doesn't know what to do in that case.

> It seems to me that there ought to be a ternary_iop
> routine that does for ternary ops what binary_iop does
> for binary ones.

Or we could just document the status quo.  It's a pretty esoteric case
-- numeric types are traditionally immutable so don't have to
implement inplace power at all, and non-numeric types aren't very
likely to implement ** at all, let alone **=.  The correct code would
be pretty hairy I think (the non-inplace ternary is hairy enough).

> > Do you have a use case where this does the wrong thing, or is this
> > just a theoretical musing?
> It's a theoretical musing that came out of my attempts
> to figure out whether, when one is implementing an
> nb_inplace_power slot for a new type, the first argument
> is guaranteed to "self". I need to know this so that Pyrex 
> can do the right thing.
> I've done some experiments with Python code, and it seems
> to do the right thing in terms of calling the correct
> __pow__, __rpow__ and __ipow__ methods. But I can't tell
> from that what's really going on at the typeslot level.
> I've looked at the code for the nb_inplace_power of
> instance objects (the only example I can find of such
> a method!), and it seems to assume that the first
> argument is always "self". But I can't see how this is
> guaranteed.
> In short, I'm confused!

Are you still confused after my assertion above?

--Guido van Rossum (home page: http://www.python.org/~guido/)