Explain Codes LogoExplain Codes Logo

How do I pass a variable by reference?

python
mutable
immutable
best-practices
Alex KataevbyAlex Kataev·Aug 4, 2024
TLDR

In Python, Variables work as references to the objects. When you deal with mutable objects like lists or dictionaries, changes made within a function are noticeable outside it. Immutable objects have some restrictions, they can't be altered directly. However, you can manage their changes within a mutable container.

def increment(number_container): number_container[0] += 1 # Some friendly addition here! num_container = [0] increment(num_container) print(num_container[0]) # Outputs: 1, the increment function applied its magic!

Observe how the increment function alters num_container, a list here, by upping its first element. All changes stay in place since lists are mutable.

Mutable vs Immutable: Know the Difference

Before tackling variables passing, it's important to differentiate mutable and immutable objects in Python. Here's a practical explanation:

  • Mutable objects like lists, dictionaries, and sets are flexible, you can change their content in-place.
  • Immutable objects such as integers, strings, and tuples once created, remain constant.

Immutable objects: Rebinding Tricks

Sadly, you can't change a string once it's born. But you can rebind it to a new object. Here's an example of manipulating an immutable object:

def update_text(original): return original + " updated" # The string has a terrifying transformation power, use it wisely! text = "Original" # Just an innocent string, soon to be updated... text = update_text(text) print(text) # Outputs: "Original updated"

Creating custom wrappers

To create an illusion of passing by reference, wrap values in a mutable type like a class. Here's a good old change function for you:

class Reference: def __init__(self, value): self.value = value def change(ref): ref.value = "Modified" # Yes, we can modify this! ref = Reference("Original") change(ref) print(ref.value) # Outputs: "Modified", a shocking transformation indeed!

Deep dive into Python's Reference approach

For those who want every bit of detail, Fredrik Lundh's concept of "Call by Object" is a must-read:

  1. When you pass a mutable object to a function, the function works on the identical object. In-place changes using the object's methods are visible outside the function too.

  2. Inside a function, creating a new local reference through reassignment of an immutable object leaves the external reference unaffected. However, to persist changes, make your function return the altered value and then reassign it outside the method.

  3. This understanding opens up the world of Python's namespace mechanics and provides a comprehensive view of variable references across different scopes.

Avoiding the pitfalls

Unfortunately, it's easy to stumble with Python's model. Keep an eye out for these common mistakes:

  • Rebinding a mutable reference: Internally to a function will not change the external reference.
  • Assuming changes are local: When you modify a mutable object within a function, the changes are not local to the function.
  • Misunderstanding scope: Variables within a function are in the local scope. Modifying them doesn't affect the global scope without an explicit command.

Reference passing: Best Practices

To challenge common scenarios and prevent potential errors, here's a distilled guide to best practices:

  • Explicit returns: Return and reassign to implement changes to immutable objects.
  • In-place methods: Use object methods that alter the object in-place over reassignments for mutable objects.
  • Wrap if necessary: If an object needs to be modified the way mutable objects do, wrap it in a mutable container or a custom class to mimic pass-by-reference behavior.
  • Documentation and contracts: Clearly document how your functions behave with respect to references and mutations to reduce confusion and bugs.