Understanding Python's call-by-object style of passing function arguments
The key difference is that in C-style language, a variable is a box in memory in which you put stuff. In Python, a variable is a name.
Python is neither call-by-reference nor call-by-value. It's something much more sensible! (In fact, I learned Python before I learned the more common languages, so call-by-value and call-by-reference seem very strange to me.)
In Python, there are things and there are names. Lists, integers, strings, and custom objects are all things. x
, y
, and z
are names. Writing
x = []
means "construct a new thing []
and give it the name x
". Writing
x = []
foo = lambda x: x.append(None)
foo(x)
means "construct a new thing []
with name x
, construct a new function (which is another thing) with name foo
, and call foo
on the thing with name x
". Now foo
just appends None
to whatever it received, so this reduces to "append None
to the the empty list". Writing
x = 0
def foo(x):
x += 1
foo(x)
means "construct a new thing 0
with name x
, construct a new function foo
, and call foo
on x
". Inside foo
, the assignment just says "rename x
to 1 plus what it used to be", but that doesn't change the thing 0.
Others have already posted good answers. One more thing that I think will help:
x = expr
evaluates expr
and binds x
to the result. On the other hand:
x.operate()
does something to x
and hence can change it (resulting in the same underlying object having a different value).
The funny cases come in with things like:
x += expr
which translate into either x = x + expr
(rebinding) or x.__iadd__(expr)
(modifying), sometimes in very peculiar ways:
>>> x = 1
>>> x += 2
>>> x
3
(so x
was rebound, since integers are immutable)
>>> x = ([1], 2)
>>> x
([1], 2)
>>> x[0] += [3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> x
([1, 3], 2)
Here x[0]
, which is itself mutable, was mutated in-place; but then Python also attempted to mutate x
itself (as with x.__iadd__
), which errored-out because tuples are immutable. But by then x[0]
was already mutated!