Your Sunday, upgraded. Free live AI class · 5 PM ISTReserve your seat
PROGRAMMING LANGUAGES

Understand Python Name Binding Before You Debug a Changed Variable

Many confusing Python bugs come from treating variables as boxes. Learn how names bind to objects, why some changes appear in two places and how to make copying deliberate.

On this page

The core idea

In Python, an assignment does not copy a value into a box. It binds a name to an object. Two names can therefore refer to the same list, dictionary or custom object. When you change that object through one name, every other name bound to it sees the change. Rebinding a name with a new assignment is different: it points only that name at a different object and leaves the original object untouched. Mutable objects such as lists, dictionaries and sets can be changed in place. Immutable objects such as integers, strings and tuples cannot, so operations on them create new objects. Once you separate 'change the object' from 'rebind the name', a large group of surprising bugs becomes predictable.

A practical approach

Step 1

When a value changes unexpectedly, ask two questions: which object does each name refer to, and did the code mutate that object or rebind a name? Print id(name) for the names involved; the same id means the same object.

Step 2

Look for in-place operations on mutable objects: append, extend, update, sort, item assignment and the += operator on lists. These change the shared object. Methods such as sorted(), or a list built with a comprehension, return a new object instead.

Step 3

Check function boundaries. Arguments are passed as references to the caller's objects, so a function that appends to a list parameter changes the caller's list. Decide whether a function should modify its input or return a new value, and name it so the choice is obvious.

Step 4

Make copies explicit when you need independence. list(items), items.copy() and dict(settings) create shallow copies; copy.deepcopy() also copies nested objects. Use a deep copy only when nested values must be independent, because it costs more and can copy things you did not intend.

Step 5

Avoid mutable default arguments. A default such as def add(item, bucket=[]) creates one list when the function is defined, and every call shares it. Use None as the default and create a new list inside the function.

Worked example

A learning platform stores default course settings in a dictionary and builds each learner's settings with learner_settings = defaults, then sets learner_settings['language'] = 'hi'. Soon every learner appears to prefer Hindi. The assignment did not copy anything: both names refer to the same dictionary, and the update changed the shared defaults. Replacing the line with learner_settings = {**defaults, 'language': 'hi'} creates a new dictionary for each learner and leaves the defaults intact. A small test that creates two learners and checks that their settings differ would have caught the bug immediately.

A mistake to avoid

Do not 'fix' shared-state bugs by sprinkling deepcopy everywhere. It hides the real design question, which is who owns each object and who is allowed to change it. Excessive copying also slows code that handles large data and can break objects that hold connections or file handles.

Try it yourself

Write a function that takes a list of scores and returns them normalised between 0 and 1. Implement it twice: once by modifying the list in place and once by returning a new list. Write a test for each that checks whether the caller's original list changed, and decide which version you would expose in a library.

Your implementation checklist

  • I can tell whether a line mutates an object or rebinds a name
  • Functions that modify their inputs say so in their name or docstring
  • Copies are made deliberately, with shallow or deep copying chosen for a reason
  • No function uses a mutable object as a default argument
  • A test proves that shared defaults are not changed by individual updates

Share or cite this guide

Innovate with Ajay Editorial (2026-10-07). Understand Python Name Binding Before You Debug a Changed Variable. https://innovatewithajay.com/blogs/understand-python-name-binding

Editorial approach, sources and corrections

YOUR NEXT CHAPTER

You have the curiosity.
Let’s turn it into capability.

Start with a practical workshop or bring a specific question to Ajay.

Chat with Ajay