x += 1といった累算代入文でxが一度しか評価されないというのはどういう意味ですか？


後半のインプレース演算の意味はわかるのですが、`x` が一度しか評価されないという意味がわかりません。  
値については、どちらの方法でもxの初期値とx+1の評価をすることにはかわりないと思うのですが。

[7.2.1. 累算代入文 (augmented assignment
statement)](https://docs.python.org/ja/3/reference/simple_stmts.html#grammar-
token-augmented-assignment-stmt)

> `x += 1` のような累算代入式は、 `x = x + 1`
> のように書き換えてほぼ同様の動作にできますが、厳密に等価にはなりません。累算代入の方では、 `x`
> は一度しか評価されません。また、実際の処理として、可能ならば インプレース (in-place)
> 演算が実行されます。これは、代入時に新たなオブジェクトを生成してターゲットに代入するのではなく、以前のオブジェクトの内容を変更するということです。

> An augmented assignment expression like x += 1 can be rewritten as x = x + 1
> to achieve a similar, but not exactly equal effect. In the augmented
> version, x is only evaluated once. Also, when possible, the actual operation
> is performed in-place, meaning that rather than creating a new object and
> assigning that to the target, the old object is modified instead.

皆さんの意見を加味した追加情報です。

## int計算

どちらでも計算時間かわらなかったので省略

次はリストに要素追加していくバージョン。

## リスト（累算）

augmentassign.py

計算にかかった時間：0.0144632940292358398[sec]

[code]

    import time
    firstTime = time.time()
    i=0
    list1=[1,1,1]
    while i <100_000:
        i+=1
        list1 +=[1]
    
    usedTime = time.time() - firstTime
    print ("計算にかかった時間:{0}".format(usedTime) + "[sec]")
    
[/code]

## リスト（ノーマル）

normalassign.py

計算にかかった時間:25.47399997711816[sec]

[code]

    import time
    firstTime = time.time()
    i=0
    list1=[1,1,1]
    while i <100_000:
        i+=1
        list1=list1+[1]
    
    usedTime = time.time() - firstTime
    print ("計算にかかった時間:{0}".format(usedTime) + "[sec]")
    
[/code]

これは単純にin-placeかどうかで圧倒的な差がついているのかと思われます。

## タプル

単なる要素の追加ではどちらでも計算時間かわらなかったので省略  
どちらもout-placeだからでしょうか。

それでは、もっと元になるオペランド自体の計算量を増やしてみることにします。  
念のためにlistのidがかわっていないことの確認を入れています。

## オペランドに計算要素をもたせたリスト（ノーマル型）

evalOnLeft_normal.py

計算前のid:140108440645824  
list1:[1000001, 1000001, 1000001, 1000001, 1000001, 1000001, 1000001, 1000001,
1000001, 1000001]  
計算後のid:140108440645824  
計算にかかった時間:3.2987570762634277[sec]

[code]

    import time
    
    firstTime = time.time()
    i=0
    list1=[1,1,1,1,1,1,1,1,1,1]
    print("計算前のid:{0}".format(id(list1)))
    
    def evalOnLeft():
        global i
        whichIndex=i%10
        return whichIndex
    
    
    while i <10_000_000:
      
        list1[evalOnLeft()]=list1[evalOnLeft()]+1
        i+=1
    
    usedTime = time.time() - firstTime
    print("list1:{0}".format(list1))
    print("計算後のid:{0}".format(id(list1)))
    print("計算にかかった時間:{0}".format(usedTime) + "[sec]")
    
[/code]

## オペランドに計算要素をもたせたリスト（累算型）

evalOnLeft_augment.py

計算前のid:140579620745408  
list1:[1000001, 1000001, 1000001, 1000001, 1000001, 1000001, 1000001, 1000001,
1000001, 1000001]  
計算後のid:140579620745408  
計算にかかった時間:2.5201609134674072[sec]

[code]

    import time
    
    firstTime = time.time()
    i=0
    list1=[1,1,1,1,1,1,1,1,1,1]
    print("計算前のid:{0}".format(id(list1)))
    
    def evalOnLeft():
        global i
        whichIndex=i%10
        return whichIndex
    
    
    while i <10_000_000:
      
        list1[evalOnLeft()]+=1
        i+=1
    
    usedTime = time.time() - firstTime
    print("list1:{0}".format(list1))
    print("計算後のid:{0}".format(id(list1)))
    print("計算にかかった時間:{0}".format(usedTime) + "[sec]")
    
[/code]

ということで、もっともらしい時間差がつきました。  
オペランド自体の計算量が増えれば、気にする価値が出てくるかもしれません。

あとは、これを累算型とノーマル型で「評価回数」が異なるというかどうかの言葉の定義であったり、実際docの作成者の意図がどうであったかの問題かもしれません。




「`x`
が一度しか評価されない」については、字句通りに`x`を1つの識別子として理解するのではなく、累積代入文の左辺に来得る任意のものを表していると解釈した方がわかりやすくなるのではないでしょうか。

[code]

    i = 0
    def inc():
        global i
        i += 1
        return i
    list = [1, 2, 3]
    list[inc()] += 1
    print(list) //->[1, 3, 3]
    
[/code]

このような例であれば、`list[inc()] += 1`と`list[inc()] = list[inc()] +
1`の違いははっきりとわかるかと思います。

