PEP 203 - Augmented Assignments

Eric Jacobs none at none
Thu Jul 20 23:53:38 EDT 2000


Now that the PEPs are up on the web, we can tear them down in
c.l.py... :)


[regarding the current usage of __add__ and __radd__ vs allowing
objects to override a += operator]

> 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.

But it would only be allowed to modify itself in either case if
the statement would have deallocated the old object! That can't
be known from looking at the expression!

Suppose x is a list. The proposal would have

    y = x
    x = x + [7, 8, 9]

and

    y = x
    x += [7, 8, 9]

doing different things!


That may be okay, of course, but the difficulty is that in C,
x += really _is_ a short hand for x = x +, and people would be
liable to use it that way in Python. They'd be changing the
meaning of their programs without realizing it... lots of bugs
and unnecessary confusion there.

Now, I do understand the rationale for the proposal... I don't
want to be saying:

    PEP[203].ThumbsDown = PEP[203].ThumbsDown + 1

;)

I'm afraid that PEP 203 is trying to do too much and will end
up confusing new users. Here's my attempt to break down what
it seems to be trying to do:

1) Abbreviate the idiom "x = x +" in source code. Less typing,
   less errors.

2) Allow the optimization of that idiom when x is a complex
   expression. Above, PEP[203] should only need to be evaluated
   once.

3) Provide a way to see the similarity symbolically between
   mutating-causing operations and an analogous operation that
   creates a new object.

   For example, list.extend() really is doing the same operation
   as list addition, but the similarity is not indicated by the
   names, "+" and "extend".

4) Avoid creating a new object and perform mutation instead when
   the old object would have been deallocated anyway, for
   performance reasons.


(1) and (3) are user-visible changes. (2) and (4) are optimizations
that should be invisible to the end user.

If operators like += were to enter Python, I would highly recommend
that they mean the same as in C, because that's where most users
are familiar with them. That is, "x += 1" can be used as a substitute
for "x = x + 1". Lazy fingers will want to substitute that!

If we allow optimization (2), then that wouldn't be quite correct
though. "x += 1" is really (in C-like terminology) "p = &x; *p =
*p + 1". In 99% of cases this wouldn't make a difference in the
result, so (2) might be okay. In addition, it looks more correct
from the users' point of view; in "x += 1" it looks like the expression
x should only be evaluated once.

(3) is something I've thought about. It would be nice to have a
mutating operator for all the non-mutating ones. That operator
should not look like += though, because it would not do what it
does in C. The exception would be when it would incidentally 
performs it that way in optimization (4).

Here's something that popped into my mind. Maybe we could borrow
the ! custom from Scheme: (functions whose names end with ! cause
mutations, I'm corrupting it for operators)

       list +! [5,6,7]

       list *! 3

to extend or triple a list in place, respectively. Of course

       int +! 1

       tuple +! (9,)

would not work.

(4) is a decision that would have to be made at run-time, and
should have nothing to do with the syntax used in the source.
If another reference to the old object is out there, the trick
can't be used.

It's also noteworthy that (4) would apply to other cases than
augmented assignment, such as

       l = [1] + [2,3] + [4,5,6] + [7,8,9]

This could save having to create (and throw away) intermediate
objects. [1], [1,2,3] and [1,2,3,4,5,6] get deleted when they
could be extended.

For this reason I'm led to think that (4) really has nothing
to do with augmented assignment, and is a more general
optimization.

Maybe something like:

	def __add__(self, other, throwaway=0):
		if throwaway:
			self.data.extend(other)
#		or  self.data +! other
			return self
		else:
			...


Or that logic could be incorporated in the bytecode
interpreter itself (if we have standardized method names
for what I'm calling +! here.)



More information about the Python-list mailing list