Python variable scope is the set of rules that determines where a variable name is visible and which value it refers to at any point in your program. When you define a variable inside a function, that variable is not visible to the rest of your program. The name exists only inside the function body, for the duration of the function call, and it disappears when the function returns. This isolation is not a bug or a limitation; it is a deliberate design choice that makes functions reliable building blocks. If every variable in your program were visible everywhere, changing one line of code could break a function defined in a completely different file, and you would never be able to trust that a function's internal name choices were safe from interference.
Python's scope system uses a four-level lookup that is easy to remember with the acronym LEGB: Local, Enclosing, Global, and Built-in. When Python encounters a variable name in your code, it searches these four scopes in order, and the first match it finds determines which value the name refers to. This search happens at runtime, not when you define the function, which means the same function can behave differently depending on what global variables exist at the moment it is called. Relying on this behavior, however, is a recipe for fragile code, and the best practice is to design functions that depend primarily on their parameters rather than on variables defined elsewhere.
Understanding scope is the natural next step after learning about Python variables and how to define and call Python functions. Variables taught you how to create and assign names. Functions taught you how to create named blocks of code. Scope explains why those two concepts interact the way they do, why functions are safe containers for local state, and when you need to reach outside a function to access a variable defined elsewhere.
Local scope: variables inside a function
Every function call creates a fresh local scope. Parameters and any variables assigned inside the function body live in this local scope and exist only for the duration of that specific call. When the function returns, the local scope is destroyed, and the variables it contained are gone. A variable defined inside a function cannot be accessed from outside, and attempting to do so raises a NameError because the name does not exist in any scope that the calling code can reach.
Each function call gets its own independent copy of the local scope. If you call the same function ten times, Python creates and destroys ten separate local scopes, and no call can see or interfere with the local variables of any other call. This independence is what makes recursive functions possible, and it is also what makes functions safe to call from multiple places in your program without worrying about unintended interactions between the calls.
Global scope: variables at the module level
A variable assigned at the top level of a Python file, outside any function, is a global variable. Global variables belong to the module they are defined in and are visible everywhere in that module, including inside functions. A function can read a global variable without any special syntax, which is convenient for constants that are genuinely shared across many functions, such as configuration values and threshold numbers that should be easy to find and change in one place.
While reading globals is allowed, reassigning them requires an explicit declaration. If a function tries to assign to a global variable without the global keyword, Python treats the assignment as creating a new local variable with the same name, leaving the global variable unchanged. This can lead to a confusing UnboundLocalError when the function tries to read the variable before assigning to it, because Python has already decided the name is local based on the presence of the assignment. The global keyword tells Python to use the global variable rather than creating a local one:
counter = 0
def increment():
global counter
counter = counter + 1The global keyword must appear before any use of the variable in the function body. It is a declaration of intent: "I am about to modify a global variable, and I am doing it deliberately." Most Python style guides recommend minimizing the use of global because functions that modify global state are harder to test, harder to reason about, and harder to reuse in different contexts.
Enclosing scope: nested functions
Python allows you to define a function inside another function. The inner function has access to variables in the outer function's scope, and this access pattern is called closure. The outer function's variables are in the enclosing scope, which Python checks after the local scope and before the global scope:
def make_multiplier(factor):
def multiply(number):
return number * factor
return multiply
double = make_multiplier(2)
print(double(5))The inner function multiply uses factor, which is a parameter of the outer function make_multiplier. When make_multiplier returns multiply, the inner function retains access to factor even though the outer function has finished executing. The variable factor is preserved as long as the inner function exists, held in the enclosing scope.
The enclosing scope has its own version of the global keyword, called nonlocal. If an inner function needs to reassign a variable in the enclosing scope rather than just read it, it must declare the variable as nonlocal. Without this declaration, Python treats the assignment as creating a new local variable inside the inner function, leaving the enclosing variable unchanged.
The built-in scope
The built-in scope contains all the names that Python makes available without any import: functions like print, len, range, and type, constants like True, False, and None, and exception types like ValueError and TypeError. These names are available in every Python file because they live in a scope that Python checks last, after local, enclosing, and global.
You can shadow a built-in name by creating a variable or function with the same name in a higher-priority scope. Defining a function called len creates a global name that shadows the built-in len, and any code in that module that calls len will call your function instead of the built-in. Shadowing built-in names is almost always a mistake, and linters flag it as a warning.
Best practices for variable scope
The cleanest functions are those that depend only on their parameters and return a result. They do not read global variables, they do not modify global state, and they do not rely on the enclosing scope. A function written this way is called a pure function, and it has the valuable property that its behavior depends only on its inputs. Given the same arguments, a pure function always returns the same result, which makes it predictable, testable, and easy to reason about.
Global variables are sometimes the right solution, particularly for configuration constants that genuinely need to be accessible across an entire module, but they should be the exception rather than the rule. If information needs to flow from one function to another, pass it through arguments and return values rather than through global variables. This explicit data flow makes the dependencies between functions visible in their signatures, which is far easier to understand and maintain than implicit dependencies through shared global state. When you do use global variables for configuration, mark them clearly with ALL_CAPS names, a Python convention that signals immutability.
Rune AI
Key Insights
- Python resolves variable names using the LEGB order: Local, Enclosing, Global, Built-in.
- Variables defined inside a function are local to that function and cannot be accessed from outside.
- A function can read global variables but must use the global keyword to reassign them.
- The nonlocal keyword lets a nested function reassign a variable in its enclosing function's scope.
- Minimize global variables; prefer passing values through parameters and return statements instead.
Frequently Asked Questions
What is variable scope in Python?
Can a Python function access variables defined outside of it?
What is the difference between local and global variables in Python?
Conclusion
Variable scope is the set of rules that determines where a variable name can be used and which value it refers to at any point in your program. Python's LEGB rule, Local first, then Enclosing, then Global, then Built-in, creates clear boundaries between functions so that local variables stay local and global access requires an explicit declaration. Understanding scope is what lets you write functions with confidence, knowing that a variable name inside one function will not accidentally interfere with the same name in another.
More in this topic
Define and Call Python Functions
Learn the exact syntax for defining and calling Python functions with the def keyword, including naming rules, the function body, and how the call stack works.
Python Function Parameters and Arguments
Learn the difference between parameters and arguments, how to pass values by position and by keyword, and the rules for default parameter values.
Python Functions Explained
Learn what Python functions are, why they matter for organizing code, and how they form the foundation of non-trivial programs.