I can see three fundamental Python concepts that can shine some light on the question:
1) First, an assignment from a mutable object like in
self.foo = arg1
is like copying a pointer (and not the value pointed to): self.foo
and arg1
are the same object. That's why the line that follows,
self.foo.age = 23
modifies arg1
(i.e. Person1
). Variables are thus different "names" or "labels" that can point to a unique object (here, a person
object). This explains why baz(Person1)
modifies Person1.age
and bar1.foo.age
to 27, since Person1
and bar1.foo
are just two names for the same person
object (Person1 is bar1.foo
returns True
, in Python).
2) The second important notion is that of assignments. In
def teh(arg1):
arg1 = [3,2,1]
variable arg1
is local, so that the code
meh = [1,2,3]
teh(meh)
first does arg1 = meh
, which means that arg1
is an additional (local) name for list meh
; but doing arg1 = [3, 2, 1]
is like saying "I changed my mind: arg1
will from now on be the name of a new list, [3, 2, 1]". The important thing to keep in mind here is that assignments, despite being denoted with an "equal" sign, are asymmetrical: they give to a (mutable) object on the right-and-side an additional name, given in the left-hand side (that's why you can't do [3, 2, 1] = arg1
, as the left-hand side must be a name [or names]). So, arg1 = meh; arg1 = [3, 2, 1]
cannot change meh
.
3) The last point is related to the question title: "passing by value" and "passing by reference" are not concepts that are relevant in Python. The relevant concepts are instead "mutable object" and "immutable object". Lists are mutable, while numbers are not, which explains what you observe. Also, your Person1
and bar1
objects are mutable (that's why you can change the person's age). You can find more information about these notions in a text tutorial and a video tutorial. Wikipedia also has some (more technical) information. An example illustrates the difference of behavior between mutable and immutable:
x = (4, 2)
y = x # This is equivalent to copying the value (4, 2), because tuples are immutable
y += (1, 2, 3) # This does not change x, because tuples are immutable; this does y = (4, 2) + (1, 2, 3)
x = [4, 2]
y = x # This is equivalent to copying a pointer to the [4, 2] list
y += [1, 2, 3] # This also changes x, because x and y are different names for the same (mutable) object
The last line is not equivalent to y = y + [1, 2, 3]
because this would only put a new list object in variable y
instead of changing the list referred to by both y
and x
.
The three concepts above (variables as names [for mutable objects], asymmetrical assignment, and mutability/immutability) explain many of Python's behaviors.