Augmented assignment for Python
The Patch
The patch, which is based on Michael
Hudsons' earlier work, can
be found here:
http://www.xs4all.nl/~thomas/python/inplaceassign2.patch
This patch is as closed to finished as I can make it. There is probably room
for optimization, but that goes for the whole of Python ;) An earlier
version of the patch was available directly from my homepage, and was the
subject of some discussion on the python-list.
This version adds most of the items listed in my original posting,
including the ability for extention types to define in-place
operator-functions, API functions to modify objects in-place and
disassembler support.
What's New
- Sunday August 6: Big change to the inner workings of the patch: use
'INPLACE_*' opcodes to do 'inplace' operations, and use normal LOAD and
STORE opcodes instead of the GETSET_* opcodes. This eliminates the need for
2-argument opcodes, and simplifies the changes to ceval.c.
- Sunday July 23: Updated for the current, fully ANSIfied CVS tree.
Hopefully the last huge-breakage of the patch<wink>
- Sunday July 16: Updated for current CVS snapshot again. I'm writing an
official Python Enhancement Proposal on the subject of augmented assignment,
PEP-203. A first draft should be in the CVS tree (in python/nondist/peps)
sometime monday.
- Monday July 10: Updated for current CVS snapshot, partial ANSIfication
and all. The longer 2.0 is delayed, the more chance this patch goes in
before it ;-)
- Friday June 30: Updated for almost-2.0b1-CVS-tree, and altered the
changes to compile.c to be less intrusive, which also shrunk the patch
30%. Uploaded this version to SourceForge, even though it will not be added
until after Python 2.0.
- Saturday June 24: Updated for cycle-GC changes in CVS tree.
- Tuesday June 20: New version of the patch, current with the CVS tree as
of Tuesday June 20, 2200 GMT. The new diff is also in 'context diff' format,
which is what Guido prefers for patches.
- Added PySequence_InPlaceConcat and PySequence_InPlaceRepeat functions to
abstract.c.
What it is
Augmented assignment is the generic name for the C operators (which also
appear in many other languages) which fold assignment and a binary operation
into a single operator. What this means is that instead of using
spam = spam + eggs
one can use the shorter, and (most people say) more obvious,
spam += eggs
Using this 'augmented assignment' notation also provides another benifit:
The ability for the compiler to see that the operation could be performed
'in-place'. Some languages, such as C++, provide a mechanism for
user-defined classes to overload the augmented-assignment operators
specifically, so that those classes can modify themselves 'in-place'.
OK, but what does that mean?
Normally, in Python, when you want to multiply an object by 10, you have to
do it like this:
vikings = vikings * 10
If the object is a user-defined class, and it defines the special method
'__mul__', this is the same as:
vikings = vikings.__mul__(10)
However, 'vikings' does not know wether it is called to modify itself, like so:
vikings = vikings * 10
or to create a new object, like so:
eric = vikings * 10
Because of this, the '__mul__' method, as well as all other 'overloaders'
for binary (two-argument) operands, have to assume they need to create a new
object. For large objects, such as large matrices, this can be a costly
operation. Augmented assignment adds the possibility of new 'special'
methods which are explicitly for in-place modification, much like Java and
C++ have them. Thus,
vikings *= 10
would be syntactic sugar for
vikings = vikings.__mul_ab__(10)
And the __mul_ab__() (_ab_ for 'and becomes') special method can choose for
itself wether to return a new object, or return 'self'.
The _ab_ in __mul_ab__, by the way, is what Guido proposed (in
this
posting) and apparently comes from an old Algol-68 naming convention. I
would personally prefer something that emphasizes 'in-place' more than
'becomes', but the names can always be changed.
Additionally, the new assignment operators only evaluate the object being
assigned to once. For example:
branches = 0
def larch():
global branches
branches = branches + 1
return branches
l = [1, 2, 3, 4]
l[larch()] += 1
would calculate 'larch()', store that value, use it to extract an item out
of l, do the augmented assignment on that object, and store it in
the original place. In contrast:
l[larch()] = l[larch()] + 1
would store and retrieve different items. To make it work like the +=
version, you need to use a temporary variable to store the index.
What the patch adds
The patch adds the following new operators (and their 'special methods' for
Python classes):
+= (__add_ab__)
-= (__sub_ab__)
*= (__mul_ab__)
/= (__div_ab__)
%= (__mod_ab__)
**= (__pow_ab__)
|= (__or_ab__)
&= (__and_ab__)
^= (__xor_ab__)
>>= (__rshift_ab__)
<<= (__lshift_ab__)
In each case, if the object in question does not define an 'in-place'
operation, the interpreter falls back to the normal operation.
In
addition, new slots are added to the PyNumberMethods and PySequenceMethods
structs, and a new flag to the tp_flags PyType struct member (for binary
compatibility). Using these new struct members, builtin types and extention
types can add the C-equivalent of the overload operators.
The patch
also adds support for these in-place assignment operators to the
list object, so that:
l = [1,2,3,4]
l += [5,6,7]
Is the same as:
l = [1,2,3,4]
l.extend([5,6,7])
And '*=', which is an in-place repeat (currently not possible, except by
'l.extend(l)' in a for loop):
l = [1,2,3,4]
l2 = l
l *= 2
after which l ends up as [1,2,3,4,1,2,3,4], and l and
l2 still refer to the same object.
Note that even though the other builtin types do not define in-place
operands, the augmented-assignment operators still work! The resulting
object will simply be a new object, instead of the old object.
How to install
The patch is relative to the current (on June 20th 2000) CVS tree, and is
fairly extensive, so it might easily fail on older trees. To use it:
If you encounter any problems, feel free to email me. Also if you encounter bugs, or
if you
want to tell me howmuch (or how little) you like this change, feel free to
email (at thomas@xs4all.net)