Update PEP to reflect reality. Explain the current mechanism the InPlace functions use to find the right function to call, and when to coerce or not.
This commit is contained in:
parent
a448946a31
commit
f32f15658a
97
pep-0203.txt
97
pep-0203.txt
|
@ -63,25 +63,76 @@ Proposed semantics
|
||||||
x += y
|
x += y
|
||||||
|
|
||||||
tries to call x.__iadd__(y), which is the `in-place' variant of
|
tries to call x.__iadd__(y), which is the `in-place' variant of
|
||||||
__add__. If __iadd__ is not present, x.__add__(y) is
|
__add__. If __iadd__ is not present, x.__add__(y) is attempted,
|
||||||
attempted, and finally y.__radd__(x) if __add__ is missing too.
|
and finally y.__radd__(x) if __add__ is missing too. There is no
|
||||||
There is no `right-hand-side' variant of __iadd__, because that
|
`right-hand-side' variant of __iadd__, because that would require
|
||||||
would require for `y' to know how to in-place modify `x', which is
|
for `y' to know how to in-place modify `x', which is unsafe to say
|
||||||
unsafe to say the least. The __iadd__ hook should behave similar
|
the least. The __iadd__ hook should behave similar to __add__,
|
||||||
to __add__, returning the result of the operation (which could be
|
returning the result of the operation (which could be `self')
|
||||||
`self') which is to be stored in the variable `x'.
|
which is to be assigned to the variable `x'.
|
||||||
|
|
||||||
For C extension types, the `hooks' are members of the
|
For C extension types, the `hooks' are members of the
|
||||||
PyNumberMethods and PySequenceMethods structures, and are called
|
PyNumberMethods and PySequenceMethods structures. Some special
|
||||||
in exactly the same manner as the existing non-inplace operations,
|
semantics apply to make the use of these methods, and the mixing
|
||||||
including argument coercion. C methods should also take care to
|
of Python instance objects and C types, as unsurprising as
|
||||||
return a new reference to the result object, whether it's the same
|
possible.
|
||||||
object or a new one. So if the original object is returned, it
|
|
||||||
should be INCREF()'d appropriately.
|
In the generic case of `x <augop> y' (or a similar case using the
|
||||||
|
PyNumber_InPlace API functions) the principal object being
|
||||||
|
operated on is `x'. This differs from normal binary operations,
|
||||||
|
where `x' and `y' could be considered `co-operating', because
|
||||||
|
unlike in binary operations, the operands in an in-place operation
|
||||||
|
cannot be swapped. However, in-place operations do fall back to
|
||||||
|
normal binary operations when in-place modification is not
|
||||||
|
supported, resuling in the following rules:
|
||||||
|
|
||||||
|
- If the left-hand object (`x') is an instance object, and it
|
||||||
|
has a `__coerce__' method, call that function with `y' as the
|
||||||
|
argument. If coercion succeeds, and the resulting left-hand
|
||||||
|
object is a different object than `x', stop processing it as
|
||||||
|
in-place and call the appropriate function for the normal binary
|
||||||
|
operation, with the coerced `x' and `y' as arguments. The result
|
||||||
|
of the operation is whatever that function returns.
|
||||||
|
|
||||||
|
If coercion does not yield a different object for `x', or `x'
|
||||||
|
does not define a `__coerce__' method, and `x' has the
|
||||||
|
appropriate `__ihook__' for this operation, call that method
|
||||||
|
with `y' as the argument, and the result of the operation is
|
||||||
|
whatever that method returns.
|
||||||
|
|
||||||
|
- Otherwise, if the left-hand object is not an instance object,
|
||||||
|
but its type does define the in-place function for this
|
||||||
|
operation, call that function with `x' and `y' as the arguments,
|
||||||
|
and the result of the operation is whatever that function
|
||||||
|
returns.
|
||||||
|
|
||||||
|
Note that no coercion on either `x' or `y' is done in this case,
|
||||||
|
and it's perfectly valid for a C type to receive an instance
|
||||||
|
object as the second argument; that is something that cannot
|
||||||
|
happen with normal binary operations.
|
||||||
|
|
||||||
|
- Otherwise, process it exactly as a normal binary operation (not
|
||||||
|
in-place), including argument coercion. In short, if either
|
||||||
|
argument is an instance object, resolve the operation through
|
||||||
|
`__coerce__', `__hook__' and `__rhook__'. Otherwise, both
|
||||||
|
objects are C types, and they are coerced and passed to the
|
||||||
|
appropriate function.
|
||||||
|
|
||||||
|
- If no way to process the operation can be found, raise a
|
||||||
|
TypeError with an error message specific to the operation.
|
||||||
|
|
||||||
|
- Some special casing exists to account for the case of `+' and
|
||||||
|
`*', which have a special meaning for sequences: for `+',
|
||||||
|
sequence concatenation, no coercion what so ever is done if a C
|
||||||
|
type defines sq_concat or sq_inplace_concat. For `*', sequence
|
||||||
|
repeating, `y' is converted to a C integer before calling either
|
||||||
|
sq_inplace_repeat and sq_repeat. This is done even if `y' is an
|
||||||
|
instance, though not if `x' is an instance.
|
||||||
|
|
||||||
|
The in-place function should always return a new reference, either
|
||||||
|
to the old `x' object if the operation was indeed performed
|
||||||
|
in-place, or to a new object.
|
||||||
|
|
||||||
[XXX so I am accepting this, but I'm a bit worried about the
|
|
||||||
argument coercion. For x+=y, if x supports augmented assignment,
|
|
||||||
y should only be cast to x's type, not the other way around!]
|
|
||||||
|
|
||||||
Rationale
|
Rationale
|
||||||
|
|
||||||
|
@ -133,7 +184,7 @@ Rationale
|
||||||
Augmented assignment won't solve all the problems for these
|
Augmented assignment won't solve all the problems for these
|
||||||
packages, since some operations cannot be expressed in the limited
|
packages, since some operations cannot be expressed in the limited
|
||||||
set of binary operators to start with, but it is a start. A
|
set of binary operators to start with, but it is a start. A
|
||||||
different PEP[3] is looking at adding new operators.
|
different PEP[2] is looking at adding new operators.
|
||||||
|
|
||||||
|
|
||||||
New methods
|
New methods
|
||||||
|
@ -194,7 +245,7 @@ New methods
|
||||||
|
|
||||||
Implementation
|
Implementation
|
||||||
|
|
||||||
The current implementation of augmented assignment[2] adds, in
|
The current implementation of augmented assignment[1] adds, in
|
||||||
addition to the methods and slots already covered, 13 new bytecodes
|
addition to the methods and slots already covered, 13 new bytecodes
|
||||||
and 13 new API functions.
|
and 13 new API functions.
|
||||||
|
|
||||||
|
@ -272,16 +323,6 @@ Open Issues
|
||||||
bytecode at this time.
|
bytecode at this time.
|
||||||
|
|
||||||
|
|
||||||
It is not possible to do an inplace operation in the variant of
|
|
||||||
|
|
||||||
<builtin type> += <instance object>
|
|
||||||
|
|
||||||
Instead, the instance objects' __radd__ hook is called, with the
|
|
||||||
builtin type as argument. The same goes for the other operations.
|
|
||||||
It might necessary to add a right-hand version of __add_ab__ after
|
|
||||||
all, to support something like that.
|
|
||||||
|
|
||||||
|
|
||||||
Copyright
|
Copyright
|
||||||
|
|
||||||
This document has been placed in the public domain.
|
This document has been placed in the public domain.
|
||||||
|
|
Loading…
Reference in New Issue