list in a tuple

Arnaud Delobelle arnodel at googlemail.com
Wed Dec 26 13:11:46 EST 2007


On Dec 26, 1:08 am, Steven D'Aprano <st... at REMOVE-THIS-
cybersource.com.au> wrote:
> On Mon, 24 Dec 2007 18:01:53 -0800, Raymond Hettinger wrote:
[...]
> > The first succeeds and the second fails.
>
> And this is a good idea?
>
> Shouldn't the tuple assignment raise the exception BEFORE calling
> __iadd__ on the item, instead of after?

If you look at the bytecode generated, this doesn't seem possible:

>>> def f():
...     a = ([1],)
...     a[0] += [2]
...
>>> import dis
>>> dis.dis(f)
  2           0 LOAD_CONST               1 (1)
              3 BUILD_LIST               1
              6 BUILD_TUPLE              1
              9 STORE_FAST               0 (a)

  3          12 LOAD_FAST                0 (a)
             15 LOAD_CONST               2 (0)
             18 DUP_TOPX                 2
             21 BINARY_SUBSCR
             22 LOAD_CONST               3 (2)
             25 BUILD_LIST               1
             28 INPLACE_ADD
             29 ROT_THREE
             30 STORE_SUBSCR
             31 LOAD_CONST               0 (None)
             34 RETURN_VALUE

BINARY_SUBSCR puts a[0] on the stack, it has no way to know that a[0]
will be changed in place.  To allow an exception to be thrown before
the in-place modification of a[0], there should be a new bytecode
instruction, say BINARY_SUBSCR_WITH_A_VIEW_TO_CHANGE_IN_PLACE, which
checks that the subscriptable object supports STORE_SUBSCR (;-).

[...]

> I was never a big fan of augmented assignments. I think it goes against
> the Python grain: it's an implied operation, using punctuation, for the
> sole (?) benefit of saving a keystroke or three.
>
> But I think this behaviour counts as a wart on the language, rather than
> a bug.

Yes.  I didn't realise this before you mentioned it, but the culprit
here seems to be the augmented assignment which acts differently on
mutable and immutable objects:

b = a  # say a is immutable
a += c # equivalent to a = a + c
b is a # -> False

b = a  # Now say a is mutable
a += c # equivalent to a.__iadd__(c)
b is a # -> True

OTOH augmented assignent are a slight optimisation:

a[i] += 1

will look for the value of a and i only once and duplicate them on the
stack, whereas

a[i] = a[i] + 1

will need to resolve a and i twice (which can be costly if a and i are
globals)

--
Arnaud




More information about the Python-list mailing list