Incomplete version of the augmented assignment PEP. It currently includes
only the arguments for/against inclusion of augmented assignment in Python, not yet the technical details. I decided to upload this half-completed version to give people some time to respond before I finish it :-) Feel free to bring up any and all arguments. The point of the PEP is to archive those, after all!
This commit is contained in:
parent
685d4508ed
commit
cf9d818d03
142
pep-0203.txt
142
pep-0203.txt
|
@ -6,6 +6,148 @@ Python-Version: 2.0
|
||||||
Status: Incomplete
|
Status: Incomplete
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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, and provides URLs for further information, where
|
||||||
|
appropriate. The CVS revision history of this file contains the
|
||||||
|
definitive historical record.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The Origin of Augmented Assignment
|
||||||
|
|
||||||
|
Augmented assignment refers to binary operators that combine two
|
||||||
|
existing operators: the assignment operator, and one of the binary
|
||||||
|
operators. Its origins lie in other programming languages, most
|
||||||
|
notably `C', where it was defined for performance reasons. They
|
||||||
|
are meant to replace the repetetive syntax of, for instance,
|
||||||
|
adding the number '1' to a variable:
|
||||||
|
|
||||||
|
x = x + 1;
|
||||||
|
|
||||||
|
with an expression that is shorter, less error-prone and easier to
|
||||||
|
optimize (by the compiler):
|
||||||
|
|
||||||
|
x += 1;
|
||||||
|
|
||||||
|
The same goes for all other binary operands, resulting in the
|
||||||
|
following augmented assignment operator list, based on Python's
|
||||||
|
current binary operator list:
|
||||||
|
|
||||||
|
+=, -=, /=, *=, %=, **=, >>=, <<=, &=, |=, ^=
|
||||||
|
|
||||||
|
See the documentation of each operator on what they do.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Augmented Assignment in Python
|
||||||
|
|
||||||
|
The traditional reasons for augmented assignment, readability and
|
||||||
|
optimization, are not as obvious in Python, for several reasons.
|
||||||
|
|
||||||
|
- Numbers are immutable, they cannot be changed. In other
|
||||||
|
programming languages, a variable holds a value, and altering
|
||||||
|
the variable changes the value it holds. In Python, variables
|
||||||
|
hold `references' to values, and altering an immutable value
|
||||||
|
means changing the variable, not what it points to.
|
||||||
|
|
||||||
|
- Assignment is a different operation in Python. In most
|
||||||
|
languages, variables are containers, and assignment copies a
|
||||||
|
value into that container. In Python, assignment binds a value
|
||||||
|
to a name, it does not copy the value into a new storage space.
|
||||||
|
|
||||||
|
- The augmented assignment operators map fairly directly into the
|
||||||
|
underlying hardware. Python does not deal directly with the
|
||||||
|
hardware it runs on, so this `natural inclusion' does not make
|
||||||
|
sense.
|
||||||
|
|
||||||
|
- The augmented assigment syntax is subtly different in more
|
||||||
|
complex expressions. What to do, for instance, in a case such
|
||||||
|
as this:
|
||||||
|
|
||||||
|
seq[i:calc(seq, i)] *= r
|
||||||
|
|
||||||
|
It is unclear whether 'seq' gets indexed once or twice, and
|
||||||
|
whether 'calc' gets called once or twice.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Normal operators
|
||||||
|
|
||||||
|
There are, however, good reasons to include augented assignment.
|
||||||
|
One of these has to do with Python's way of handling operators. In
|
||||||
|
Python, a user defined class can implement one or more of the
|
||||||
|
binary operators by supplying a 'magic' method name. For instance,
|
||||||
|
for a class to support '<instance> + <object>', the '__add__'
|
||||||
|
method should be defined. This method should return a new object,
|
||||||
|
which is the result of the expression.
|
||||||
|
|
||||||
|
For the case of '<object> + <instance>', where 'object' does not
|
||||||
|
have an '__add__' method, the class can define a '__radd__'
|
||||||
|
method, which then should behave exactly as '__add__'. Indeed,
|
||||||
|
'__radd__' is often a different name for the same method.
|
||||||
|
|
||||||
|
For C extention types, a similar technique is available, through
|
||||||
|
the PyNumberMethods and PySequenceMethods members of the PyType
|
||||||
|
structure.
|
||||||
|
|
||||||
|
However, the problem with this approach is that the '__add__'
|
||||||
|
method cannot know in what context it is called. It cannot tell
|
||||||
|
whether it should create a new object, or whether it is allowed to
|
||||||
|
modify itself. (As would be the case in 'x = x + 1') As a result,
|
||||||
|
the '__add__' method, and all other such 'magic' methods, should
|
||||||
|
always return a new object. For large objects, this can be very
|
||||||
|
inefficient.
|
||||||
|
|
||||||
|
This inefficiency is often solved by adding a method that does the
|
||||||
|
appropriate modification 'in-place'. List objects, for instance,
|
||||||
|
have the 'extend' method that behaves exactly as the '+' operator,
|
||||||
|
except the operation is done on the list itself, instead of on a
|
||||||
|
copy.
|
||||||
|
|
||||||
|
The augmented assignment syntax can support this behaviour
|
||||||
|
explicitly. When the magic method for 'in-place' operation are
|
||||||
|
missing, it can fall back to the normal methods for that
|
||||||
|
operation, maintaining full backward compatibility even when
|
||||||
|
mixing the new syntax with old objects.
|
||||||
|
|
||||||
|
The other benifit of augmented assignment is readability. After
|
||||||
|
the general concept of augmented assignment is grasped, all the
|
||||||
|
augmented assigment operators instantly become obvious. There is
|
||||||
|
no need for non-obvious and non-standard method names to implement
|
||||||
|
efficient, in-place operations, and there is no need to check the
|
||||||
|
type of an object before operating on it: the augmented assignment
|
||||||
|
will work for all types that implement that basic operation, not
|
||||||
|
merely those that implement the augmented variant.
|
||||||
|
|
||||||
|
And the last problem with augmented assignment, what to do with
|
||||||
|
indexes and function calls in the expression, can be solved in a
|
||||||
|
very Pythonic manner: if it looks like it's only called once, it
|
||||||
|
*is* only called once. Taking this expression:
|
||||||
|
|
||||||
|
seq[func(x)] += x
|
||||||
|
|
||||||
|
The function 'func' is called once, and 'seq' is indexed twice:
|
||||||
|
once to retrieve the value (__getitem__), and once to store it
|
||||||
|
(__setitem__). So the expression can be rewritten as:
|
||||||
|
|
||||||
|
tmp = func(x)
|
||||||
|
seq[tmp] = seq[tmp] + x
|
||||||
|
|
||||||
|
The augmented assignment form of this expression is much more
|
||||||
|
readable.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Local Variables:
|
Local Variables:
|
||||||
mode: indented-text
|
mode: indented-text
|
||||||
|
|
Loading…
Reference in New Issue