Having issues with when objects are changed and when they aren't in Python. Here is my poorly contrived example below:
class person:
age = 21
class bar:
def __init__(self, arg1):
self.foo = arg1
self.foo.age = 23
def baz(arg1):
arg1.age = 27
def teh(arg1):
arg1 = [3,2,1]
Person1 = person()
bar1 = bar(Person1)
print Person1.age
print bar1.foo.age
baz(Person1)
print Person1.age
print bar1.foo.age
meh = [1,2,3]
teh(meh)
print meh
The output is
23
23
27
27
[1, 2, 3]
So when we declare Person1, Person1.age is 21. A reference to this object is passed to the class constructor of another class instance of bar, called bar1. Any changes made to this reference will change Person1.
This is also the case when we pass Person1 to a normal function, the Person1.age now equals 27.
But why doesn't this work on the variable "meh"? Certainly, if we assign a variable a = meh
and change a = [6, 6, 6]
, then meh will also be changed. I'm confused. Is there any literature on how all this works?
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
is like copying a pointer (and not the value pointed to):
self.foo
andarg1
are the same object. That's why the line that follows,modifies
arg1
(i.e.Person1
). Variables are thus different "names" or "labels" that can point to a unique object (here, aperson
object). This explains whybaz(Person1)
modifiesPerson1.age
andbar1.foo.age
to 27, sincePerson1
andbar1.foo
are just two names for the sameperson
object (Person1 is bar1.foo
returnsTrue
, in Python).2) The second important notion is that of assignments. In
variable
arg1
is local, so that the codefirst does
arg1 = meh
, which means thatarg1
is an additional (local) name for listmeh
; but doingarg1 = [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 changemeh
.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
andbar1
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:The last line is not equivalent to
y = y + [1, 2, 3]
because this would only put a new list object in variabley
instead of changing the list referred to by bothy
andx
.The three concepts above (variables as names [for mutable objects], asymmetrical assignment, and mutability/immutability) explain many of Python's behaviors.
No, actually, it won't:
It's the difference between overwriting a variable and modifying the instance pointed to by a variable.
Lists, dictionaries, sets, and objects are mutable types. If you add, remove, set, get, or otherwise modify something in an instance of them, it updates everything that references that instance.
If, however, you assign a completely new instance of the type to a variable, that changes the reference stored in that variable, and thus the old referenced instance is not changed.
Python does not have pass-by-value, nor pass-by-reference, but instead uses pass-by-object -- in other words, objects are passed directly into functions, and bound to the parameter name given in the function definition.
When you do spam = "green", you have bound the name spam to the string object "green"; if you then do eggs = spam you have not copied anything, you have not made reference pointers; you have simply bound another name, eggs, to the same object ("green" in this case). If you then bind spam to something else (spam = 3.14159) eggs will still be bound to "green".
In your
teh
function you are not changing/modifying/mutating the passed in object, you are reassigning the namearg1
to a different list. To changearg1
you want this: