How do I pass a variable by reference in Python?
I wrote this class for testing:
class PassByReference:
def __init__(self):
self.variable = 'Original'
self.change(self.variable)
print(self.variable)
def change(self, var):
var = 'Changed'
When I tried creating an instance, the output was Original. So it seems like parameters in Python are passed by value. Is that correct? How can I modify the code to get the effect of pass-by-reference, so that the output is Changed?
Sometimes people are surprised that code like x = 1, where x is a parameter name, doesn’t impact on the caller’s argument, but code like x[0] = 1 does. This happens because item assignment and slice assignment are ways to mutate an existing object, rather than reassign a variable, despite the = syntax. See Why can a function modify some arguments as perceived by the caller, but not others? for details.
See also What’s the difference between passing by reference vs. passing by value? for important, language-agnostic terminology discussion.
Python doesn’t support traditional pass-by-reference like languages such as C++ or Java. Instead, Python uses a pass-by-object-reference mechanism, where you pass references to objects rather than the objects themselves. Your code doesn’t work because strings are immutable in Python - when you assign var = 'Changed', you’re creating a new string object and binding the parameter var to it, rather than modifying the original object.
Contents
- Understanding Python’s Parameter Passing Mechanism
- Why Your Code Doesn’t Work: Immutable Objects
- How to Achieve Pass-by-Reference Behavior
- Mutable vs Immutable Objects in Function Calls
- Practical Examples and Best Practices
- Common Pitfalls and Solutions
Understanding Python’s Parameter Passing Mechanism
Python uses a pass-by-assignment approach, which combines elements of both pass-by-value and pass-by-reference behaviors. When you pass an argument to a function, Python creates a binding between the function parameter name and the object that was passed.
As Real Python explains: “Some languages handle function arguments as references to existing variables, which is known as pass by reference. Other languages handle them as independent values, an approach known as pass by value.”
The key insight is that Python always passes object references. Whether this behaves like pass-by-value or pass-by-reference depends on whether the object is mutable or immutable:
- Mutable objects (lists, dictionaries, sets): Can be modified in place
- Immutable objects (strings, numbers, tuples): Cannot be changed after creation
Why Your Code Doesn’t Work: Immutable Objects
Your code fails because self.variable is a string, which is immutable in Python. When you call change(self.variable), Python creates a new binding where the parameter var refers to the same string object as self.variable. However, when you execute var = 'Changed', you’re not modifying the original string - you’re creating a new string and rebinding var to point to it.
The Medium article on Python parameter passing explains this perfectly: “With a = 3, we have the variable a referring to the object with value 3.”
Here’s what happens step by step:
# In your __init__ method:
self.variable = 'Original' # Creates string object 'Original'
self.change(self.variable) # Passes reference to 'Original' string
# In change method:
def change(self, var): # var now refers to same object as self.variable
var = 'Changed' # Creates new string 'Changed', var now points to it
# Original 'Original' string remains unchanged
How to Achieve Pass-by-Reference Behavior
To achieve the effect you want, you need to modify the instance attribute directly rather than trying to pass the string value. Here are several approaches:
Method 1: Pass the instance itself
class PassByReference:
def __init__(self):
self.variable = 'Original'
self.change(self)
print(self.variable)
def change(self, instance):
instance.variable = 'Changed'
Method 2: Use a mutable container
class PassByReference:
def __init__(self):
self.variable = ['Original'] # Use list instead of string
self.change(self.variable)
print(self.variable[0])
def change(self, var):
var[0] = 'Changed' # Modify the list contents
Method 3: Return the new value and assign it
class PassByReference:
def __init__(self):
self.variable = 'Original'
self.variable = self.change(self.variable)
print(self.variable)
def change(self, var):
return 'Changed'
Mutable vs Immutable Objects in Function Calls
The behavior you’re experiencing stems from the fundamental difference between mutable and immutable objects in Python:
Mutable Objects (Can be modified)
- Lists, dictionaries, sets, custom classes
- When passed to functions, their contents can be modified
- The original object in the caller’s scope reflects these changes
def modify_list(lst):
lst.append('new element') # Modifies the original list
my_list = [1, 2, 3]
modify_list(my_list)
print(my_list) # Output: [1, 2, 3, 'new element']
Immutable Objects (Cannot be modified)
- Strings, numbers, tuples
- When passed to functions, any assignment creates new objects
- The original object in the caller’s scope remains unchanged
def modify_string(s):
s = s + ' modified' # Creates new string, doesn't affect original
my_string = 'hello'
modify_string(my_string)
print(my_string) # Output: 'hello' (unchanged)
As GeeksforGeeks states: “Whether the original data changes depends on whether the object is mutable (can be changed in place) or immutable (cannot be changed once created).”
Practical Examples and Best Practices
Example 1: Modifying a Dictionary
def add_to_dict(d, key, value):
d[key] = value # This modifies the original dictionary
my_dict = {'name': 'Alice'}
add_to_dict(my_dict, 'age', 25)
print(my_dict) # Output: {'name': 'Alice', 'age': 25}
Example 2: Modifying a List Contents
def modify_list_contents(lst):
lst[0] = 'changed' # This works - modifies existing list
# lst = ['new'] # This doesn't work - creates new list reference
my_list = ['original', 2, 3]
modify_list_contents(my_list)
print(my_list) # Output: ['changed', 2, 3]
Example 3: The “Reassignment” Trap
def reassign_parameter(param):
param = 'new value' # This only affects local parameter
my_var = 'original'
reassign_parameter(my_var)
print(my_var) # Output: 'original' - unchanged
Common Pitfalls and Solutions
Pitfall 1: Confusing Assignment with Mutation
def problematic_function(x):
x = 10 # Assignment - creates new object
x[0] = 10 # Mutation - modifies existing object (only works for mutable)
# For immutable objects, assignment doesn't affect caller
# For mutable objects, mutation affects caller but assignment doesn't
Pitfall 2: Mutable Default Arguments
def bad_function(items=[]): # Dangerous - same list object reused
items.append('item')
return items
print(bad_function()) # ['item']
print(bad_function()) # ['item', 'item'] - problem!
# Solution: Use None as default
def good_function(items=None):
if items is None:
items = []
items.append('item')
return items
Solution: Use Clear Documentation
def process_data(data):
"""
Modifies the passed data object in place.
For immutable data, returns modified copy.
"""
if isinstance(data, (str, int, float, tuple)):
# Handle immutable objects by returning new object
return f"processed_{data}"
else:
# Handle mutable objects by modifying in place
data.processed = True
return data # Return for consistency
Conclusion
To summarize the key points about Python’s parameter passing mechanism:
- Python uses pass-by-object-reference, not traditional pass-by-value or pass-by-reference
- Immutable objects (strings, numbers, tuples) cannot be modified - any assignment creates new objects
- Mutable objects (lists, dictionaries, sets) can be modified in place, affecting the original object
- To achieve the effect you want in your code, either pass the instance itself, use mutable containers, or return new values
- The key distinction is between reassignment (which always creates new bindings) and mutation (which can modify existing objects for mutable types)
For your specific case, the best solution would be to pass the instance to the change method rather than the instance attribute, as shown in Method 1 above. This gives you direct access to modify the object’s state rather than trying to modify immutable string values.
Sources
- Pass by reference vs value in Python - GeeksforGeeks
- Understanding Python’s Pass by Assignment - Medium
- Pass-by-value, reference, and assignment - mathspp
- Pass by Reference in Python: Background and Best Practices – Real Python
- Tricky Python II: Parameter Passing for Mutable & Immutable Objects - Medium
- Python: Passing mutable object to method - Stack Overflow
- Why can a function modify some arguments as perceived by the caller, but not others? - Stack Overflow