Augmented Assignment Operators Python Found

PEP 203 -- Augmented Assignments

PEP:203
Title:Augmented Assignments
Author:thomas at python.org (Thomas Wouters)
Status:Final
Type:Standards Track
Created:13-Jul-2000
Python-Version:2.0
Post-History:14-Aug-2000

Introduction

This PEP describes the augmented assignment proposal for Python 2.0. This PEP tracks the status and ownership of this feature, slated for introduction in Python 2.0. It contains a description of the feature and outlines changes necessary to support the feature. This PEP summarizes discussions held in mailing list forums [1], and provides URLs for further information where appropriate. The CVS revision history of this file contains the definitive historical record.

Proposed Semantics

The proposed patch that adds augmented assignment to Python introduces the following new operators:

+= -= *= /= %= **= <<= >>= &= ^= |=

They implement the same operator as their normal binary form, except that the operation is done in-place when the left-hand side object supports it, and that the left-hand side is only evaluated once.

They truly behave as augmented assignment, in that they perform all of the normal load and store operations, in addition to the binary operation they are intended to do. So, given the expression:

x += y

The object is loaded, then is added to it, and the resulting object is stored back in the original place. The precise action performed on the two arguments depends on the type of , and possibly of .

The idea behind augmented assignment in Python is that it isn't just an easier way to write the common practice of storing the result of a binary operation in its left-hand operand, but also a way for the left-hand operand in question to know that it should operate on itself, rather than creating a modified copy of itself.

To make this possible, a number of new hooks are added to Python classes and C extension types, which are called when the object in question is used as the left hand side of an augmented assignment operation. If the class or type does not implement the in-place hooks, the normal hooks for the particular binary operation are used.

So, given an instance object , the expression:

x += y

tries to call , which is the in-place variant of . If is not present, is attempted, and finally if is missing too. There is no right-hand-side variant of , because that would require for to know how to in-place modify , which is unsafe to say the least. The hook should behave similar to , returning the result of the operation (which could be ) which is to be assigned to the variable .

For C extension types, the hooks are members of the and structures. Some special semantics apply to make the use of these methods, and the mixing of Python instance objects and C types, as unsurprising as possible.

In the generic case of (or a similar case using the API functions) the principal object being operated on is . This differs from normal binary operations, where and 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, resulting in the following rules:

  • If the left-hand object () is an instance object, and it has a method, call that function with as the argument. If coercion succeeds, and the resulting left-hand object is a different object than , stop processing it as in-place and call the appropriate function for the normal binary operation, with the coerced and as arguments. The result of the operation is whatever that function returns.

    If coercion does not yield a different object for , or does not define a method, and has the appropriate for this operation, call that method with 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 and as the arguments, and the result of the operation is whatever that function returns.

    Note that no coercion on either or 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 , and . 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 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 or . For , sequence repeating, is converted to a C integer before calling either and . This is done even if is an instance, though not if is an instance.

The in-place function should always return a new reference, either to the old object if the operation was indeed performed in-place, or to a new object.

Rationale

There are two main reasons for adding this feature to Python: simplicity of expression, and support for in-place operations. The end result is a tradeoff between simplicity of syntax and simplicity of expression; like most new features, augmented assignment doesn't add anything that was previously impossible. It merely makes these things easier to do.

Adding augmented assignment will make Python's syntax more complex. Instead of a single assignment operation, there are now twelve assignment operations, eleven of which also perform a binary operation. However, these eleven new forms of assignment are easy to understand as the coupling between assignment and the binary operation, and they require no large conceptual leap to understand. Furthermore, languages that do have augmented assignment have shown that they are a popular, much used feature. Expressions of the form:

<x> = <x> <operator> <y>

are common enough in those languages to make the extra syntax worthwhile, and Python does not have significantly fewer of those expressions. Quite the opposite, in fact, since in Python you can also concatenate lists with a binary operator, something that is done quite frequently. Writing the above expression as:

<x> <operator>= <y>

is both more readable and less error prone, because it is instantly obvious to the reader that it is that is being changed, and not that is being replaced by something almost, but not quite, entirely unlike .

The new in-place operations are especially useful to matrix calculation and other applications that require large objects. In order to efficiently deal with the available program memory, such packages cannot blindly use the current binary operations. Because these operations always create a new object, adding a single item to an existing (large) object would result in copying the entire object (which may cause the application to run out of memory), add the single item, and then possibly delete the original object, depending on reference count.

To work around this problem, the packages currently have to use methods or functions to modify an object in-place, which is definitely less readable than an augmented assignment expression. Augmented assignment won't solve all the problems for these packages, since some operations cannot be expressed in the limited set of binary operators to start with, but it is a start. A different PEP [3] is looking at adding new operators.

New methods

The proposed implementation adds the following 11 possible hooks which Python classes can implement to overload the augmented assignment operations:

__iadd__ __isub__ __imul__ __idiv__ __imod__ __ipow__ __ilshift__ __irshift__ __iand__ __ixor__ __ior__

The i in stands for in-place.

For C extension types, the following struct members are added.

To :

binaryfunc nb_inplace_add; binaryfunc nb_inplace_subtract; binaryfunc nb_inplace_multiply; binaryfunc nb_inplace_divide; binaryfunc nb_inplace_remainder; binaryfunc nb_inplace_power; binaryfunc nb_inplace_lshift; binaryfunc nb_inplace_rshift; binaryfunc nb_inplace_and; binaryfunc nb_inplace_xor; binaryfunc nb_inplace_or;

To :

binaryfunc sq_inplace_concat; intargfunc sq_inplace_repeat;

In order to keep binary compatibility, the TypeObject member is used to determine whether the TypeObject in question has allocated room for these slots. Until a clean break in binary compatibility is made (which may or may not happen before 2.0) code that wants to use one of the new struct members must first check that they are available with the macro:

if (PyType_HasFeature(x->ob_type, Py_TPFLAGS_HAVE_INPLACE_OPS) && x->ob_type->tp_as_number && x->ob_type->tp_as_number->nb_inplace_add) { /* ... */

This check must be made even before testing the method slots for values! The macro only tests whether the slots are available, not whether they are filled with methods or not.

Implementation

The current implementation of augmented assignment [2] adds, in addition to the methods and slots already covered, 13 new bytecodes and 13 new API functions.

The API functions are simply in-place versions of the current binary-operation API functions:

PyNumber_InPlaceAdd(PyObject *o1, PyObject *o2); PyNumber_InPlaceSubtract(PyObject *o1, PyObject *o2); PyNumber_InPlaceMultiply(PyObject *o1, PyObject *o2); PyNumber_InPlaceDivide(PyObject *o1, PyObject *o2); PyNumber_InPlaceRemainder(PyObject *o1, PyObject *o2); PyNumber_InPlacePower(PyObject *o1, PyObject *o2); PyNumber_InPlaceLshift(PyObject *o1, PyObject *o2); PyNumber_InPlaceRshift(PyObject *o1, PyObject *o2); PyNumber_InPlaceAnd(PyObject *o1, PyObject *o2); PyNumber_InPlaceXor(PyObject *o1, PyObject *o2); PyNumber_InPlaceOr(PyObject *o1, PyObject *o2); PySequence_InPlaceConcat(PyObject *o1, PyObject *o2); PySequence_InPlaceRepeat(PyObject *o, int count);

They call either the Python class hooks (if either of the objects is a Python class instance) or the C type's number or sequence methods.

The new bytecodes are:

INPLACE_ADD INPLACE_SUBTRACT INPLACE_MULTIPLY INPLACE_DIVIDE INPLACE_REMAINDER INPLACE_POWER INPLACE_LEFTSHIFT INPLACE_RIGHTSHIFT INPLACE_AND INPLACE_XOR INPLACE_OR ROT_FOUR DUP_TOPX

The bytecodes mirror the bytecodes, except that they are implemented as calls to the API functions. The other two bytecodes are utility bytecodes: behaves like except that the four topmost stack items are rotated.

is a bytecode that takes a single argument, which should be an integer between 1 and 5 (inclusive) which is the number of items to duplicate in one block. Given a stack like this (where the right side of the list is the top of the stack):

[1, 2, 3, 4, 5]

would duplicate the top 3 items, resulting in this stack:

[1, 2, 3, 4, 5, 3, 4, 5]

with an argument of 1 is the same as . The limit of 5 is purely an implementation limit . The implementation of augmented assignment requires only with an argument of 2 and 3, and could do without this new opcode at the cost of a fair number of and .

Open Issues

The API is only a subset of the normal API: only those functions that are required to support the augmented assignment syntax are included. If other in-place API functions are needed, they can be added later.

The bytecode is a conveniency bytecode, and is not actually necessary. It should be considered whether this bytecode is worth having. There seems to be no other possible use for this bytecode at this time.

Copyright

This document has been placed in the public domain.

Source: https://github.com/python/peps/blob/master/pep-0203.txt

Augmented assignment (or compound assignment) is the name given to certain assignmentoperators in certain programming languages (especially those derived from C). An augmented assignment is generally used to replace a statement where an operator takes a variable as one of its arguments and then assigns the result back to the same variable. A simple example is which is expanded to . Similar constructions are often available for various binary operators.

In general, in languages offering this feature, most operators that can take a variable as one of their arguments and return a result of the same type have an augmented assignment equivalent that assigns the result back to the variable in place, including arithmetic operators, bitshift operators, and bitwise operators.

Discussion[edit]

For example, the following statement or some variation of it can be found in many programs:

x = x + 1

This means "find the number stored in the variable x, add 1 to it, and store the result of the addition in the variable x." As simple as this seems, it may have an inefficiency, in that the location of variable x has to be looked up twice if the compiler does not recognize that two parts of the expression are identical: x might be a reference to some array element or other complexity. In comparison, here is the augmented assignment version:

x += 1

With this version, there is no excuse for a compiler failing to generate code that looks up the location of variable x just once, and modifies it in place, if of course the machine code supports such a sequence. For instance, if x is a simple variable, the machine code sequence might be something like

Load x Add 1 Store x

and the same code would be generated for both forms. But if there is a special op code, it might be

MDM x,1

meaning "Modify Memory" by adding 1 to x, and a decent compiler would generate the same code for both forms. Some machine codes offer INC and DEC operations (to add or subtract one), others might allow constants other than one.

More generally, the form is

x ?= expression

where the ? stands for some operator (not always +), and there may be no special op codes to help. There is still the possibility that if x is a complicated entity the compiler will be encouraged to avoid duplication in accessing x, and of course, if x is a lengthy name, there will be less typing required. This last was the basis of the similar feature in the ALGOL compilers offered via the Burroughs B6700 systems, using the tilde symbol to stand for the variable being assigned to, so that

LongName:=x + sqrt(LongName)*7;

would become

LongName:=x + sqrt(~)*7;

and so forth. This is more general than just x:=~ + 1; Producing optimum code would remain the province of the compiler.

Semantics[edit]

In expression-oriented programming languages such as C, assignment and augmented assignment are expressions, which have a value. This allows their use in complex expressions. However, this can produce sequences of symbols that are difficult to read or understand, and worse, a mistype can easily produce a different sequence of gibberish that although accepted by the compiler does not produce desired results. In other languages, such as Python, assignment and augmented assignment are statements, not expressions, and thus cannot be used in complex expressions. For example, the following is valid C, but not valid Python:

As with assignment, in these languages augmented assignment is a form of right-associative assignment.

By language[edit]

C descendants[edit]

In C, C++, and C#, the assignment operator is =, which is augmented as follows:

OperatorDescription
Addition
Subtraction
Multiplication
Division
Modulus
Left bit shift
Right bit shift
Bitwise AND
Bitwise exclusive OR
Bitwise inclusive OR

Each of these is called a compound assignment operator in said languages.[1][2][3]

Supporting languages[edit]

The following list, though not complete or all-inclusive, lists some of the major programming languages that support augmented assignment operators.

See also[edit]

References[edit]

0 Thoughts to “Augmented Assignment Operators Python Found

Leave a comment

L'indirizzo email non verrĂ  pubblicato. I campi obbligatori sono contrassegnati *