Question
Python Argument Passing Explained: Pass by Reference vs Rebinding
Question
I wrote the following Python class to test whether a variable can be passed by reference:
class PassByReference:
def __init__(self):
self.variable = 'Original'
self.change(self.variable)
print(self.variable)
def change(self, var):
var = 'Changed'
When I create an instance, the output is Original, not Changed.
Does this mean Python passes parameters by value? If not, how does Python actually pass arguments to functions and methods?
Also, how can I change this code to produce the effect people usually mean by "pass by reference," so that the final output becomes Changed?
I am also confused about why code like this does not affect the caller:
x = 1
but code like this can affect the caller:
x[0] = 1
Why does assignment to a parameter name behave differently from item assignment?
Short Answer
By the end of this page, you will understand how Python passes arguments, why assigning to a parameter does not change the caller's variable, and why mutating a mutable object can affect the caller. You will also learn practical ways to get "pass-by-reference-like" behavior in Python using objects, containers, and return values.
Concept
Python does not use traditional pass-by-reference in the same way as languages like C++ references. It also is not best described as simple pass-by-value in the everyday beginner sense.
The most useful beginner-friendly explanation is:
- Python passes object references by assignment.
- A function receives a new local name that refers to the same object as the caller's argument.
- If you rebind that local name, the caller is unaffected.
- If you mutate the shared object, the caller can observe the change.
In your example:
def change(self, var):
var = 'Changed'
var starts by referring to the same string object as self.variable. But then var = 'Changed' makes var refer to a different string. It does not change self.variable.
This matters because Python code constantly passes lists, dictionaries, class instances, strings, numbers, and functions into other functions. To write correct code, you need to know whether you are:
- changing the object itself, or
- just changing which object a local variable points to
A key detail is that strings, integers, and tuples are immutable. They cannot be changed in place. So if you want a different string value, you must create a new string object and assign a name to it.
Mental Model
Think of a variable name as a sticky note attached to an object.
When you call a function, Python does not send the sticky note itself. Instead, it gives the function a new sticky note pointing to the same object.
So inside the function:
- If you move your sticky note to a new object, the caller's sticky note stays where it was.
- If you edit the shared object, both sticky notes still point to that edited object.
Example:
- Caller's note:
self.variable -> 'Original' - Function parameter note:
var -> 'Original'
Then inside the function:
var = 'Changed'
Now the function's sticky note points to a new string:
var -> 'Changed'self.variable -> 'Original'
The original object was never changed.
Now compare that with a list:
- Caller's note:
items -> [1, 2, 3] - Function note:
x -> [1, 2, 3]
If the function does:
x[0] = 99
Syntax and Examples
Core idea
Rebinding a parameter
def change_value(x):
x = 'Changed'
name = 'Original'
change_value(name)
print(name) # Original
This does not change name, because x is only a local name.
Mutating a mutable object
def change_list(items):
items[0] = 'Changed'
values = ['Original']
change_list(values)
print(values[0]) # Changed
This works because the list object is mutable and is changed in place.
Your class example
If your goal is to change an attribute on the object, the simplest fix is to modify self.variable directly:
class PassByReference:
def __init__(self):
.variable =
.change()
(.variable)
():
.variable =
PassByReference()
Step by Step Execution
Consider this example:
def change(var):
var = 'Changed'
name = 'Original'
change(name)
print(name)
Step 1: create name
name = 'Original'
name refers to the string 'Original'.
Step 2: call the function
change(name)
A new local variable var is created inside change. It refers to the same string object as name.
At this moment:
name -> 'Original'var -> 'Original'
Step 3: rebind the local parameter
var = 'Changed'
Real World Use Cases
Understanding rebinding vs mutation is important in real Python programs.
1. Updating objects in classes
class User:
def __init__(self, name):
self.name = name
def rename(self, new_name):
self.name = new_name
You usually modify the object's attributes, not a copied parameter name.
2. Editing lists during data processing
def normalize(scores):
for i in range(len(scores)):
scores[i] = max(scores[i], 0)
This mutates the list in place.
3. Modifying dictionaries in configuration code
def set_defaults(config):
config.setdefault('timeout', 30)
config.setdefault('retries', 3)
A function can update a shared dictionary directly.
Real Codebase Usage
In real projects, developers usually avoid trying to force pass-by-reference semantics. Instead, they use clearer Python patterns.
Common patterns
1. Return the new value
Best when working with immutable values such as strings, numbers, and tuples.
def apply_discount(price, percent):
return price * (1 - percent)
price = 100
price = apply_discount(price, 0.2)
2. Mutate a passed-in object intentionally
Useful for lists, dictionaries, and custom class instances.
def add_log_entry(logs, message):
logs.append(message)
3. Update object attributes through self
Common in classes.
class Task:
def complete(self):
self.done = True
4. Use guard clauses before mutation
():
profile :
profile[] =
Common Mistakes
1. Thinking = changes the caller's variable
Broken example:
def change(x):
x = 10
value = 5
change(value)
print(value) # still 5
Why it happens:
x = 10only rebinds the local namex.
How to avoid it:
- Return the new value instead.
def change(x):
return 10
value = 5
value = change(value)
2. Expecting immutable objects to change in place
Broken example:
def add_exclamation(text):
text += '!'
message = 'Hello'
add_exclamation(message)
print(message) # Hello
Why it happens:
Comparisons
| Concept | What it does | Affects caller? | Example |
|---|---|---|---|
| Rebinding a local variable | Makes the local name point to a new object | No | x = 'Changed' |
| Mutating a list | Changes the existing list object | Yes | x[0] = 'Changed' |
| Mutating a dict | Changes the existing dictionary | Yes | x['a'] = 1 |
| Setting an attribute | Changes the existing object | Yes | obj.name = 'New' |
| Returning a new value | Creates a result and lets caller choose whether to assign it | Only if caller reassigns |
Cheat Sheet
Quick rules
- Python passes arguments by object reference assignment.
- Function parameters are local names.
x = somethinginside a function only rebinds the local name.x[0] = ...,x['k'] = ..., andobj.attr = ...modify an existing object.- Immutable objects cannot be changed in place.
- Mutable objects can usually be changed in place.
Common patterns
Rebinding does not affect caller
def f(x):
x = 10
Mutation can affect caller
def f(items):
items.append(10)
Best way to update immutable data
def f(x):
return x + 1
Best way to update object state in a class
FAQ
Is Python pass-by-reference or pass-by-value?
Neither label is perfect by itself. The clearest explanation is that Python passes object references by assignment.
Why does x = 1 not change the caller's variable?
Because it only rebinds the local parameter name x to a new object.
Why can x[0] = 1 change the caller's data?
Because it mutates the shared object that both caller and function refer to.
How do I get pass-by-reference behavior in Python?
Usually by mutating a mutable object, changing an attribute on self, or returning a new value and assigning it.
What is the Pythonic way to update a string inside a function?
Return the new string and assign it in the caller.
Should I use a list or dictionary just to simulate pass-by-reference?
Only when it truly improves the design. Most of the time, returning a value or updating object attributes is clearer.
Why does self.variable = 'Changed' work?
Because it updates the attribute on the instance object itself, which still exists after the method returns.
Mini Project
Description
Build a small Python example that demonstrates the difference between rebinding and mutation. The project will show three common ways data can be updated in Python: returning a new immutable value, mutating a list, and changing an object attribute. This gives you a practical understanding of why Python does not behave like traditional pass-by-reference languages.
Goal
Create a script that clearly shows which operations affect the caller and which do not.
Requirements
- Create one function that tries to change a string parameter by assignment.
- Create one function that changes a list by mutating it.
- Create a class with a method that updates an instance attribute.
- Print values before and after each operation.
- Make the output clearly show the difference between rebinding and mutation.
Keep learning
Related questions
@staticmethod vs @classmethod in Python Explained
Learn the difference between @staticmethod and @classmethod in Python with clear examples, use cases, mistakes, and a mini project.
Catch Multiple Exceptions in One except Block in Python
Learn how to catch multiple exceptions in one Python except block using tuples, with examples, mistakes, and real-world usage.
Convert Bytes to String in Python 3
Learn how to convert bytes to str in Python 3 using decode(), text mode, and proper encodings with practical examples.