Chapter 11: Scope
Part 1: What is Scope? A Simple Analogy
Imagine you’re in a large apartment building.
-
Global Scope: The building’s address and name are painted on the outside. Everyone in the city can see it. This is like a global variable.
-
Local Scope: Your individual apartment has its own unique layout, furniture, and your personal belongings. Your neighbor can’t see inside your apartment or use your toothbrush. This is like a local variable inside a function.
-
Enclosing Scope: Now, imagine your apartment is within a larger, gated community. The community has its own rules and shared amenities (like a pool) that all apartments in that community can use, but people outside the community cannot. This is like an enclosing scope (e.g., a variable defined in an outer function that an inner function can access).
-
Built-in Scope: These are like the fundamental laws of physics that apply everywhere in the world, no matter which building or city you’re in. In Python, these are the built-in functions like
print(),len(), andrange().
The most important rule is that code outside a scope cannot reach in, but code inside a scope can look out.
Part 2: The Four Types of Scope in Python (The LEGB Rule)
Python, and many other languages, follows the LEGB rule to resolve variable names. It’s a search order: Local -> Enclosing -> Global -> Built-in.
Let’s break each one down with examples.
1. Local Scope
A variable created inside a function is said to be in the local scope of that function. It is only accessible from within that function.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
<span class="token keyword">def</span> <span class="token function">greet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> message <span class="token operator">=</span> <span class="token string">"Hello from inside the function!"</span> <span class="token comment"># 'message' is a local variable</span> <span class="token keyword">print</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span> <span class="token comment"># This works fine, we're inside the function</span> greet<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment"># Output: Hello from inside the function!</span> <span class="token comment"># print(message) # This would cause a NameError! 'message' is not defined outside.</span> |
Explanation: When the greet() function finishes running, the message variable is destroyed. It’s like a post-it note used during a phone call and then thrown away. The outside world has no knowledge of it.
2. Enclosing Scope (Nonlocal)
This occurs with nested functions. A variable defined in the outer function is in the enclosing scope of the inner function. The inner function can access (and with special permission, modify) it.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<span class="token keyword">def</span> <span class="token function">outer_function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> outer_var <span class="token operator">=</span> <span class="token string">"I'm from the outer function"</span> <span class="token comment"># Enclosing scope variable</span> <span class="token keyword">def</span> <span class="token function">inner_function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">print</span><span class="token punctuation">(</span>outer_var<span class="token punctuation">)</span> <span class="token comment"># Inner function can ACCESS the enclosing variable</span> inner_function<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment"># Output: I'm from the outer function</span> outer_function<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment"># print(outer_var) # This would still cause a NameError!</span> |
What about modifying an enclosing variable? You can’t just reassign it directly. You need the nonlocal keyword.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<span class="token keyword">def</span> <span class="token function">counter_maker</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> count <span class="token operator">=</span> <span class="token number">0</span> <span class="token comment"># Enclosing variable</span> <span class="token keyword">def</span> <span class="token function">increment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">nonlocal</span> count <span class="token comment"># Declare that we intend to modify the enclosing variable</span> count <span class="token operator">+=</span> <span class="token number">1</span> <span class="token keyword">return</span> count <span class="token keyword">return</span> increment <span class="token comment"># Return the inner function itself</span> my_counter <span class="token operator">=</span> counter_maker<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment"># my_counter is now the 'increment' function</span> <span class="token keyword">print</span><span class="token punctuation">(</span>my_counter<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment"># Output: 1</span> <span class="token keyword">print</span><span class="token punctuation">(</span>my_counter<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment"># Output: 2</span> <span class="token keyword">print</span><span class="token punctuation">(</span>my_counter<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment"># Output: 3</span> <span class="token comment"># This is a powerful pattern called a "closure". The 'count' variable persists</span> <span class="token comment"># between calls to my_counter, but is still hidden from the global scope.</span> |
3. Global Scope
A variable created outside of any function (at the top level of a script or module) is in the global scope. It’s accessible from anywhere in your code, both inside and outside functions.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
global_var <span class="token operator">=</span> <span class="token string">"I'm accessible everywhere!"</span> <span class="token comment"># A global variable</span> <span class="token keyword">def</span> <span class="token function">some_function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">print</span><span class="token punctuation">(</span>global_var<span class="token punctuation">)</span> <span class="token comment"># Accessing the global variable is fine</span> some_function<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment"># Output: I'm accessible everywhere!</span> <span class="token keyword">print</span><span class="token punctuation">(</span>global_var<span class="token punctuation">)</span> <span class="token comment"># Output: I'm accessible everywhere!</span> |
A common pitfall: Modifying global variables inside a function. If you try to assign a value to a variable inside a function, Python will create a new local variable by default, even if a global variable with the same name exists.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
x <span class="token operator">=</span> <span class="token number">10</span> <span class="token comment"># Global variable</span> <span class="token keyword">def</span> <span class="token function">modify_global</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> x <span class="token operator">=</span> <span class="token number">20</span> <span class="token comment"># This creates a NEW local variable 'x', it does NOT modify the global one!</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"Inside the function, x is: </span><span class="token interpolation"><span class="token punctuation">{</span>x<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span> modify_global<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment"># Output: Inside the function, x is: 20</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"Outside the function, x is: </span><span class="token interpolation"><span class="token punctuation">{</span>x<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span> <span class="token comment"># Output: Outside the function, x is: 10</span> |
To modify a global variable from inside a function, you must use the global keyword.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
x <span class="token operator">=</span> <span class="token number">10</span> <span class="token keyword">def</span> <span class="token function">actually_modify_global</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">global</span> x <span class="token comment"># Declare that we are using the global 'x'</span> x <span class="token operator">=</span> <span class="token number">20</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"Inside the function, x is now: </span><span class="token interpolation"><span class="token punctuation">{</span>x<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span> actually_modify_global<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment"># Output: Inside the function, x is now: 20</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"Outside the function, x is now: </span><span class="token interpolation"><span class="token punctuation">{</span>x<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span> <span class="token comment"># Output: Outside the function, x is now: 20</span> |
Instructor’s Warning: Overusing global variables is a recipe for disaster in larger programs. It makes your code hard to debug, hard to test, and unpredictable, as any part of your program can change the value. It’s like having a town bulletin board where anyone can write anything—it quickly becomes chaotic. Favor local variables and passing data as arguments whenever possible.
4. Built-in Scope
This is the widest scope of all. It contains names that are pre-loaded into Python when it starts. These are things like print(), len(), int(), range(), etc.
|
0 1 2 3 4 5 6 7 8 9 |
<span class="token comment"># You can use these anywhere without defining them</span> my_list <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">]</span> length <span class="token operator">=</span> <span class="token builtin">len</span><span class="token punctuation">(</span>my_list<span class="token punctuation">)</span> <span class="token comment"># 'len' is from the built-in scope</span> <span class="token keyword">print</span><span class="token punctuation">(</span>length<span class="token punctuation">)</span> <span class="token comment"># 'print' is also from the built-in scope</span> |
You can, but shouldn’t, override built-ins. It’s technically possible to create your own variable called print, but then you can’t use the original print function anymore. This is a great way to introduce confusing bugs.
|
0 1 2 3 4 5 6 7 8 9 |
<span class="token comment"># DON'T DO THIS</span> <span class="token keyword">print</span> <span class="token operator">=</span> <span class="token string">"I'm a string now, not a function"</span> <span class="token comment"># print("Hello") # This will cause a TypeError!</span> <span class="token keyword">del</span> <span class="token keyword">print</span> <span class="token comment"># Restore the original</span> |
Part 3: The LEGB Rule in Action – A Worked Example
Let’s trace through a more complex example to see the LEGB search in action.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<span class="token comment"># GLOBAL scope</span> animal <span class="token operator">=</span> <span class="token string">"fruit bat"</span> <span class="token keyword">def</span> <span class="token function">outer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token comment"># ENCLOSING scope</span> animal <span class="token operator">=</span> <span class="token string">"ouzel"</span> fruit <span class="token operator">=</span> <span class="token string">"mango"</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"In outer, before inner: animal = </span><span class="token interpolation"><span class="token punctuation">{</span>animal<span class="token punctuation">}</span></span><span class="token string">, fruit = </span><span class="token interpolation"><span class="token punctuation">{</span>fruit<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span> <span class="token keyword">def</span> <span class="token function">inner</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token comment"># LOCAL scope</span> animal <span class="token operator">=</span> <span class="token string">"dormouse"</span> <span class="token comment"># Creates a new local variable</span> veg <span class="token operator">=</span> <span class="token string">"asparagus"</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"In inner: animal = </span><span class="token interpolation"><span class="token punctuation">{</span>animal<span class="token punctuation">}</span></span><span class="token string">, fruit = </span><span class="token interpolation"><span class="token punctuation">{</span>fruit<span class="token punctuation">}</span></span><span class="token string">, veg = </span><span class="token interpolation"><span class="token punctuation">{</span>veg<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span> inner<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"In outer, after inner: animal = </span><span class="token interpolation"><span class="token punctuation">{</span>animal<span class="token punctuation">}</span></span><span class="token string">, fruit = </span><span class="token interpolation"><span class="token punctuation">{</span>fruit<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span> outer<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string-interpolation"><span class="token string">f"In global: animal = </span><span class="token interpolation"><span class="token punctuation">{</span>animal<span class="token punctuation">}</span></span><span class="token string">"</span></span><span class="token punctuation">)</span> <span class="token comment"># Trying to print 'veg' or 'fruit' here would cause a NameError.</span> |
Step-by-step execution:
-
The program starts.
animalis defined in the Global scope as"fruit bat". -
outer()is called. -
Inside
outer,animalis defined in its Enclosing scope as"ouzel". This shadows (hides) the globalanimalinside this function. -
fruitis also defined in the Enclosing scope. -
The first
printstatement runs, showing the enclosinganimalandfruit. -
inner()is called from withinouter. -
Inside
inner,animalis defined in its Local scope as"dormouse". This shadows the enclosinganimal. -
The
printstatement insideinnerneeds to find three variables:-
animal: The Local scope is checked first. Found! It’s"dormouse". -
fruit: The Local scope is checked. Not found. Next, the Enclosing scope is checked. Found! It’s"mango". -
veg: The Local scope is checked. Found! It’s"asparagus".
-
-
innerfinishes and is destroyed, along with its local variables (animalandveg). -
Back in
outer, the secondprintshows its variables.animalis still"ouzel"(the local"dormouse"is gone), andfruitis still"mango". -
outerfinishes and its variables (animal,fruit) are destroyed. -
Back in the global scope, the final
printshows the globalanimal, which is still"fruit bat".
This example perfectly illustrates the isolation and search hierarchy of scopes.
Part 4: Why Scope Matters – Best Practices
Understanding scope isn’t just an academic exercise. It’s crucial for writing good code.
-
Modularity and Predictability: By keeping variables local to the functions that need them, you create self-contained, predictable blocks of code. You can be confident that what happens inside a function stays inside, unless you explicitly return a value.
-
Preventing Naming Conflicts: In a large project with many developers, it’s impossible to avoid using the same variable name (like
countorindex) in different parts of the program. Scope ensures these variables don’t clash. Thecountin one function is completely separate from thecountin another. -
Memory Management: Local variables are created when a function starts and destroyed when it ends. This frees up memory automatically. Global variables live for the entire duration of your program.
-
Debugging: Bugs are much easier to find when you know exactly which parts of your code can affect a variable’s value. If a variable’s value is wrong, and it’s global, you have to search the entire program. If it’s local, you only have to look at one function.
Summary
-
Scope defines where a variable is visible and accessible.
-
Local Scope: Inside a function. Created and destroyed with the function call.
-
Enclosing Scope: In nested functions, the outer function’s scope is the inner function’s enclosing scope.
-
Global Scope: At the top level of a script. Accessible everywhere.
-
Built-in Scope: Contains Python’s pre-defined names.
-
LEGB Rule: The order Python searches for a variable name: Local -> Enclosing -> Global -> Built-in.
-
globalandnonlocal: Keywords used to modify variables in global and enclosing scopes from within a nested scope. Use them sparingly.
Think of scope as the organizational principle of your code. By mastering it, you take a huge step toward writing clean, efficient, and bug-free programs. Keep practicing, and soon it will become second nature!
