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.fooandarg1are 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, apersonobject). This explains whybaz(Person1)modifiesPerson1.ageandbar1.foo.ageto 27, sincePerson1andbar1.fooare just two names for the samepersonobject (Person1 is bar1.fooreturnsTrue, in Python).2) The second important notion is that of assignments. In
variable
arg1is local, so that the codefirst does
arg1 = meh, which means thatarg1is an additional (local) name for listmeh; but doingarg1 = [3, 2, 1]is like saying “I changed my mind:arg1will 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
Person1andbar1objects 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 variableyinstead of changing the list referred to by bothyandx.The three concepts above (variables as names [for mutable objects], asymmetrical assignment, and mutability/immutability) explain many of Python’s behaviors.