1

I've found that setting integer variables all at once works as expected:

foo = bar = cue = 0
foo += 1
bar += 2
print(foo, bar, cue)

1 2 0

However, when doing the same thing with a list type, it updates all of the vars at the same time. The resulting vars will always equal one another.

foo = bar = cue = []
foo += [1]
bar += [2]
print(foo, bar, cue)


[1, 2] [1, 2] [1, 2]

Why does the behavior differ depending on the variable type? Thanks much.

4
  • 1
    In the first example, you are assigning entirely new values to foo and bar; the previous value they held is completely irrelevant. In the second example, you are mutating the existing value of the variables - and since they all refer to the same object, they all see the changes. Commented Aug 13, 2022 at 0:53
  • 1
    There's no difference in the initial binding/assignment. In both cases, all variables will be pointing to the same underlying object, whether its an integer or a list. The difference is that integers are immutable, whereas lists are mutable. The += operation performs the __iadd__ method, which on lists is an in-place operation (it mutates the object), whereas for integers it will rebind the variable to a different integer object (equivalent to foo = foo + 1) Commented Aug 13, 2022 at 0:53
  • 3
    The short explanation is that += works differently for integers and lists. Commented Aug 13, 2022 at 0:54
  • They are all the same list, and the addition extends the list, it does not perform addition on the elements in the list. Commented Aug 13, 2022 at 0:59

1 Answer 1

3

The augmented assignment operator += hooks into the datamodel magic method __iadd__ ("in-place addition"). If this method doesn't exist, it falls back to using the regular __add__ method. From the docs:

These methods should attempt to do the operation in-place (modifying self) and return the result (which could be, but does not have to be, self). If a specific method is not defined, the augmented assignment falls back to the normal methods.

It happens that int doesn't define int.__iadd__, because it does not make sense to do anything "in-place" on an immutable type.

>>> int.__iadd__
AttributeError: type object 'int' has no attribute '__iadd__'

However, list does implement it, and "extends" the existing list with elements taken from iterating the right hand side:

>>> list.__iadd__
<slot wrapper '__iadd__' of 'list' objects>

So when foo == 0, the augmented assignment foo += 1 does something like this:

foo = foo.__add__(1)  # returns a different integer instance

Whereas when foo == [] the augmented assigmnent foo += [1] does something more like this:

foo = foo.__iadd__([1])  # modifies foo in-place and returns foo again

It follows that foo, bar, and cue are names bound to different integer objects in the first case, but bound to the same list instance in the second case.

Sign up to request clarification or add additional context in comments.

1 Comment

Perfectly explained and makes total sense. Thanks so much!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.