Join us and get access to hundreds of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to hundreds of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Hint: You can adjust the default video playback speed in your account settings.
Hint: You can set the default subtitles language in your account settings.
Sorry! Looks like there’s an issue with video playback 🙁 This might be due to a temporary outage or because of a configuration issue with your browser. Please see our video player troubleshooting guide to resolve the issue.

Object Passing in Python

In this lesson, you’ll learn that Python doesn’t pass objects by value or reference. Python passes objects by assignment instead. In the examples, you’ll see how this differs when the object is mutable or immutable.

00:00 In previous videos, you learned about pass-by-reference and pass-by-value. To review, if you pass an object by value, you pass a copy of its value to the function receiving it.

00:13 This requires the object to be duplicated in memory, and the receiving function will only be able to access this new memory space. When you pass an object by reference, you pass a copy of just the memory address of the object.

00:29 In other words, a pointer. This can be much smaller than the actual object itself. Additionally, the receiving function can now use that memory address to modify the value there directly. At this point, you might be wondering, are function arguments in Python pass-by-value or pass-by-reference?

00:52 The answer is neither. Arguments are what the Python documentation calls pass-by-assignment, meaning that the parameters in the function being called point to the objects in memory that were passed into them as arguments.

01:10 It looks like pass-by-reference, but you have to remember that it doesn’t pass the memory address to the value of the object. It passes the address of the entire PyObject, which itself contains the value.

01:24 Instead of passing an address to the data, we pass an address to data which contains the data.

01:33 In Python, we’re passing in a reference to a PyObject in memory, which behaves differently depending on whether the passed-in object is immutable or mutable. If you pass an immutable type, like the string or int, it mimics pass-by-value because you can’t actually modify

01:55 the object being passed in, anyway. A new object is always going to be created, which can then be returned from the function. This is why string functions often return the modified string because they can’t mutate the immutable string type directly.

02:13 If you pass a mutable type, it mimics pass-by-reference because you can directly modify the object passed into the function, without having to create a new object.

02:25 However, even if you pass a mutable type, like a list, if you reassign the parameter, instead of modifying its value, a separate object is created for it in memory, and that becomes inaccessible outside the scope of the function.

02:42 I know this is probably confusing, so let me clarify this with a code example.

02:48 As usual, I’m here in Visual Studio Code in a new Python script. I’m going to start this by defining a function called add_10, which will take a parameter and add 10 to it. Inside, I’ll use the += operator to

03:05 add 10. Outside this function, I will create a new integer name called y which will point to a PyObject with a value of 20. Then I will call add_10, passing in y, and finally, print out the value of y.

03:24 And when I run this you see that we’re still getting 20. That’s because the int type is immutable.

03:33 When we pass y to the add_10 function, what we’re really doing is telling it to create a new name x, which is the parameter, and bind it to the same object in memory that y points to. But then, because the type is immutable, incrementing x by 10 really creates a new object in memory with the value 30, and

03:57 x is set to point to it. But y does not point to it. That’s why printing the value of y returns 20 still. I’m going to modify the function to instead append 10 to the end of a list that we pass in.

04:14 Now I’ll change y to be a list of integers.

04:19 The list type is mutable, meaning that this should mimic pass-by-value behavior.

04:27 And it does! 10 now appears at the end of the list. The changes we made to x within the function are reflected in y, even though it’s outside the scope of the function. No new objects had to be created, so x and y both point to this newly modified list.

04:47 We do have to be careful with how we use this, though. This means that if we use it in a way that does not call upon its mutability, this still won’t work. To demonstrate, I’m going to remove the call to the append method, and instead, just reassign x to a new list object with random numbers.

05:10 I will run this and you see, it appears we’ve lost our pass-by-reference behavior. That’s because inside the function a new object had to be created since we reassigned the x name completely.

05:26 We didn’t modify it using its mutability, we forced Python to change what x was bound to. It follows that x and y no longer point to the same object in memory, and so these changes are not seen outside the scope of this function.

05:44 In the next two videos. I’ll show you some common ways to mimic this pointer, or pass-by-reference, behavior.

carl on July 15, 2020

At ~4:23 in Object Passing in Python you inadvertently described the list type as immutable. It’s fairly clear from the context that you meant to say mutable, but might be worth an edit .

carl on July 15, 2020

When describing pass-by-assignment, it would have been very helpful to show what that means in much the same way as you showed how assigning names to objects works.

Given this function definition and call:

def foo(v):
  v += 1

w = 1
foo(w)
print(w)

The pass-by-assignment behavior makes this roughly equivalent to

w = 1
v = w     # pass by assignment -- now v is just another name for w
v += 1    # now v is replaced by a new object containing 2
del(v)    # after we return from v, the name v no longer exists
print(w)  # w still points at the same object containing 1

Tadej P on Oct. 7, 2020

I think the explanation at 1:33 gives the wrong impression that objects are passed differently based on their mutability.

The difference is that objects of mutable types have facilities to change their internal state, while immutable ones don’t.

So the only way to change the value of a name containing an immutable type is by reassignment, at which point that name refers to a different object.

But because, at function call, the arguments get a new name object (a copy) that points to their PyObject, the original name outside the scope is not affected.

To expand on carl’s example:

>>> def foo(v):
...     print(id(v))
...     v = v+1
...     print(id(v))
... 
>>> w = 1000
>>> id(w)
140247376306096 # w points to PyObject containing 1000

>>> foo(w)
140247376306096 # v points to original PyObject containing 1000
140247376306000 # v now points to different PyObject containing 1001

>>> id(w)
140247376306096 # w is unaffected, still points to original pyObject containing 1000

Become a Member to join the conversation.