quizdog
quizdog

Reputation: 662

Implementation of += for Python lists as related to argument passing

I am reviewing a Python workbook for an author who claims the following:

Immutable parameters like integers, strings, or tuples are passed by value and any changes to these parameters within the function do not change their respective values outside the function Mutable objects, like lists and dictionaries are passed by reference and any changes to them are seen outside the function

I tried to explain that (to my understanding) all arguments are passed the
same way: as a reference to an object,

Python doesn't look to see if that object being referenced is mutable or immutable.
And, in particular, if the parameter name holding the reference to the passed-in
argument is assigned a new value that just means the reference to the original object is lost.

The author's example of Python 'doing the right thing' is given with these two functions:

def f(i)
    i += 1

x = 10
f(x)
print(x)       # prints 10 "even though parameter i was modified in f()"


def g(l):
    l += [8, 9]

y = [1, 2]
g(y)
print(y)       # prints [1, 2, 8, 9] "because l is mutable and was modified in g()"

In emailing with him he says

in f() assigning to i doesn't change the argument value
but in g() assigning to l does change the argument value
Therefore "Python must be passing them differently"

My response was to rewrite g() as what appears to be an equivalent function
and show that it does not have the same behavior

def h(l):
    l = l + [8, 9]   # on the surface appears to do the same thing as += operator ...

y = [1, 2]
h(y)
print(y)       # prints [1, 2] because is assigning to parameter `l` just discards
               # the reference to the passed in object

So in preparing for the question "then why do g() and h() behave differently?"
My assumption is that the __iadd(self, other)__ method that implements
the += for lists, if it were written Python rather than C, would essentially be:

def __iadd__(self, other):
    self.extend(other)
    

And so the more functionally equivalent rewrite of his g() would be:

def h(l):
    l.extend([8, 9])

y = [1, 2]
h(y)
print(y)     # prints [1, 2, 8, 9] because the *original* list is modified using the ref

Ok, sorry for the long wind up, but here are the questions

  1. it does seem to be a little strange that writing l += [8, 9] vs. l = l + [8, 9] have different behaviors, is there a general principle in Python to explain why that's ok?

  2. is the += operator indeed implemented as something like extend(self, other) or is there something else going on I'm missing?

Thanks!

Upvotes: 0

Views: 37

Answers (0)

Related Questions