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

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)