Python Namespace and Scope of Variable: Simple Guide with Easy Examples
If you have ever wondered why two variables with the same name can hold different values in different places, the answer is namespaces and scope. These are the rules Python uses to decide which object a name points to at any moment. In this guide, I will explain names, namespaces, and scope in plain words, with examples you can run in your Python interpreter. By the end, you will understand how Python resolves names better than most beginners.
What is a name in Python?
Before we understand namespaces, we need to know what a name is.
A name is an identifier — a label you give to an object. It is the way you access a property or an object in your program.
In Python, everything is an object. Numbers, strings, lists, functions, classes — they are all objects. A name is just a reference that points to one of these objects.
For example:
i = 10
Here, 10 is an object stored in memory. i is an identifier or name that is associated with it. The name i does not contain the value 10 — it points to the object 10 in memory.
This distinction matters. In Python, a name is not a box that holds a value. A name is a label that refers to an object. That is why we say "names refer to objects" instead of "names store values."
The id() function — seeing object addresses
Python provides a built-in function called id() that gives you the memory address of any object. This is how you can prove that two names point to the same object.
i = 10
print(id(10)) # 2031417408
print(id(i)) # 2031417408
Notice both lines print the same number. That is because i and the literal 10 refer to the same object in memory.
The exact number will be different on your machine, but the point is the same: the id() of a name matches the id() of the object it points to.
Another example with increments
def show():
i = 10
print(id(i)) # 2031417408
i = i + 1
print(id(i)) # 2031417568
j = 10
print(id(j)) # 2031417408
show()
Look at what happens:
i = 10— nameipoints to object 10 at address 2031417408i = i + 1— Python creates a new object 11. Nameinow points to 11 at a different addressj = 10— Python sees that object 10 already exists, so it just points the new namejto the same 10 object
This is a key insight. Python does not create a duplicate object for the same small immutable value. It reuses the existing one. That is why id(j) matches id(i) for the original 10.
How Python works internally with names
Let's walk through what happens step by step.
When Python executes i = 10:
- Python creates the object
10in memory (or finds it if it already exists) - Python binds the name
ito that object
When Python executes i = i + 1:
- Python reads the value of
i(which is 10) - Adds 1 to it, producing 11
- Creates the object
11in memory - Rebinds the name
ito point to the new object 11
The original object 10 is not changed. It might still exist if something else refers to it. Otherwise, Python's garbage collector will clean it up later.
When Python executes j = 10:
- Python looks for the object 10. It finds the existing one (because small integers are cached)
- Binds the name
jto the same object
This is why id(j) and id(i) matched for the original 10. They point to the same place.
The idea is simple: names are labels. Objects are the real things. Assigning a name to a value never changes the value — it only changes which object the name points to.
Names can refer to anything — even functions
In Python, a name can refer to any type of object. You can rebind the same name to different types:
i = 10
i = [1, 2, 3, 4, 5]
i = "Hello Python"
All of these are valid. The name i first points to an integer, then to a list, then to a string. The name has not changed — only what it points to.
Functions are objects too. So a name can refer to a function:
def show():
print("Welcome to Python")
x = show
x()
Here, x is a name that points to the same function object as show. Calling x() runs the function. This is the same as calling show().
Note: x = show without parentheses assigns the function object to x. x = show() with parentheses would call the function and assign its return value (which is None in this case).
This flexibility is one of the reasons Python feels so expressive. Names are just labels — they can point to anything.
What is a namespace?
Simply put, a namespace is a group of identifiers or names. It is a mapping from names to objects.
Think of a namespace like a phone book. It maps names to phone numbers. In Python, a namespace maps names to objects.
Python creates several namespaces automatically:
- Built-in namespace — created when the Python interpreter starts. It contains all the built-in names like
id(),print(),quit(), and more. This namespace exists as long as the interpreter runs. - Global namespace — each module creates its own global namespace. This is where names defined at the top level of a module live.
- Local namespace — created when a function is called. It holds the names defined inside that function.
Namespaces are isolated from each other. This is how Python avoids name collisions. You can have a variable called data in module A and another variable called data in module B. They do not clash because they live in separate namespaces.
A module can contain many functions and classes. Each function call creates its own local namespace. Each class definition also creates its own namespace for its attributes.
Here is the hierarchy, from the broadest to the narrowest:
Built-in Namespace
|
Global Namespace
|
Local Namespace
When Python looks up a name, it searches in this order: local first, then global, then built-in. If it does not find the name in any of these, it raises a NameError.
Python variable scope — local, global, built-in
A scope is the part of a program from where a namespace can be accessed directly. In simple words, scope is about where a name is visible.
Python has three main types of scope:
- Local names — declared inside a function, visible only inside that function
- Global names — declared outside any function, visible throughout the module
- Built-in names — always available, provided by Python itself
When a name or variable is declared inside a function, it lives in the local namespace. When it is declared outside any function, it lives in the global namespace.
Example: local vs global
i = 12 # global namespace
def display():
i = 100 # local namespace
print(i)
display()
print(i)
Output:
100
12
Let's understand why. When display() is called, Python creates a local namespace for that function. Inside the function, i = 100 creates a new local variable i. The local i shadows the global i. So the first print(i) inside the function prints 100.
After the function returns, the local namespace is destroyed. The second print(i) runs at the module level and sees the global i, which is still 12.
This is why the output is 100 followed by 12. The local assignment did not modify the global variable at all.
The rule is: an assignment inside a function creates a local variable by default. It does not touch the global variable of the same name.
Changing global values with the global keyword
What if you actually want to modify the global variable from inside a function? You use the global keyword.
i = 12
def display():
global i
i = 100
print(i)
display()
print(i)
Output:
100
100
Now both prints show 100. Why? Because the global i line tells Python: "Do not create a local variable called i. Use the global one instead." So when i = 100 runs, it modifies the global variable.
After the function returns, the global i is still 100. The second print confirms it.
Use the global keyword sparingly. Modifying global variables from inside functions makes your code harder to understand. But sometimes it is the right tool for the job — like a module-level configuration flag or a counter.
Reading globals with globals()
What if you want to read a global variable from inside a function without using the global keyword? Python gives you the globals() function.
globals() returns a dictionary of the current global namespace. You can access any global variable by name using dictionary syntax.
i = 12
def show():
# this creates a new local variable, not the global one
i = 20
print("local value ", i)
# access the global variable using globals()
x = globals()["i"]
print(x)
show()
print(i)
Output:
local value 20
12
12
Let's break it down:
- Inside
show(),i = 20creates a new local variable. The globaliis untouched. print("local value ", i)prints the local value: 20globals()["i"]reads the globalifrom the global namespace dictionary. It returns 12.print(x)prints 12- After the function,
print(i)prints the globali, which is still 12
The globals() function is useful when you need to read a global variable without changing it, or when you need to look up global names dynamically.
There is also a locals() function that returns the local namespace. But it is less commonly used because the local namespace changes constantly as the function runs.
Common questions
What is a namespace in Python in simple words?
A namespace in Python is simply a group of identifiers or names. It is a mapping of names to objects. A built-in namespace is created when the Python interpreter starts, each module creates its own global namespace, and a local namespace is created when a function is called.
What is the difference between local, global and built-in scope?
Local names are declared inside a function and can only be used there. Global names are declared outside any function at the module level. Built-in names are always available, like print(), id(), and quit(). Python searches from local to global to built-in when resolving a name.
How do I modify a global variable inside a function?
Use the global keyword inside the function before assigning the variable. Without global, Python treats the assignment as creating a new local variable and the global value stays unchanged.
What is the globals() function in Python?
globals() returns a dictionary of the current global namespace. You can use it to read a global variable's value from inside a function without using the global keyword, like globals()["i"].
Why does Python not create a duplicate object for the same value?
Python caches small immutable objects like small integers. When you assign the same value to a new name, both names point to the same object in memory. You can confirm this with the id() function.
What is the id() function in Python?
The id() function returns the unique memory address of an object. If two names return the same id, they refer to the same object in memory.
Where to go next
You now understand names, namespaces, and scope in Python. You know that names are labels pointing to objects. You know that built-in, global, and local namespaces form a hierarchy. You know how to use the global keyword to modify globals, and globals() to read them.
If you remember only one thing, remember this: Python looks up names in the order local, global, built-in. Assigning inside a function creates a local name by default.
The real learning happens when you experiment. Open your Python interpreter and try these examples. Change the values. Print the id() of different objects. See for yourself how names behave. That is how the concept sticks.
If this helped, bookmark it. Next, explore Python's LEGB rule — Local, Enclosing, Global, Built-in — which is the full rule Python uses for name resolution when you have nested functions.
No comments:
Post a Comment