Movatterモバイル変換


[0]ホーム

URL:


— FREE Email Series —

🐍 Python Tricks 💌

Python Tricks Dictionary Merge

🔒 No spam. Unsubscribe any time.

Browse TopicsGuided Learning Paths
Basics Intermediate Advanced
aialgorithmsapibest-practicescareercommunitydatabasesdata-sciencedata-structuresdata-vizdevopsdjangodockereditorsflaskfront-endgamedevguimachine-learningnewsnumpyprojectspythonstdlibtestingtoolsweb-devweb-scraping

Table of Contents

Recommended Course

Python Scope & the LEGB Rule: Resolving Names in Your Code

The LEGB Rule & Understanding Python Scope

33m · 9 lessons

Python Scope and the LEGB Rule: Resolving Names in Your Code

Python Scope and the LEGB Rule: Resolving Names in Your Code

byLeodanis Pozo RamosPublication date Jul 16, 2025Reading time estimate 49mintermediatepython

Table of Contents

Remove ads

Recommended Course

The LEGB Rule & Understanding Python Scope(33m)

The scope of a variable in Python determines where in your code that variable is visible and accessible. Python has four general scope levels: local, enclosing, global, and built-in. When searching for a name, Python goes through these scopes in order. It follows the LEGB rule, which stands for Local, Enclosing, Global, and Built-in.

Understanding how Python manages the scope of variables and names is a fundamental skill for you as a Python developer. It helps you avoid unexpected behavior and errors related to name collisions or referencing the wrong variable.

By the end of this tutorial, you’ll understand that:

  • Ascope in Python defines where a variable isaccessible, following the local, enclosing, global, and built-in (LEGB) rule.
  • Anamespace is a dictionary that maps names to objects and determines their scope.
  • The four scope levels—local,enclosing,global, andbuilt-in—each control variable visibility in a specific context.
  • Common scope-related built-in functions includeglobals() andlocals(), which provide access to global and local namespaces.

To get the most out of this tutorial, you should be familiar with Python concepts likevariables,functions,inner functions,exception handling,comprehensions, andclasses.

Get Your Code:Click here to download the free sample code that you’ll use to learn about Python scope and the LEGB rule.

Take the Quiz: Test your knowledge with our interactive “Python Scope and the LEGB Rule: Resolving Names in Your Code” quiz. You’ll receive a score upon completion to help you track your learning progress:


Python Scope and the LEGB Rule: Resolving Names in Your Code

Interactive Quiz

Python Scope and the LEGB Rule: Resolving Names in Your Code

Test your Python scope skills with this quiz. Practice the LEGB rule, scope vs namespace, globals vs locals, and cases like comprehensions and exceptions.

Understanding the Concept of Scope

In programming, thescope of a name defines the region of a program where you can unambiguously access that name, which could identify avariable,constant,function,class, or any other object. In most cases, you’ll only be able to access a name within its own scope or from an inner or nested scope.

Nearly all programming languages use the concept of scope to avoid name collisions and unpredictable behavior. Most often, you’ll distinguish between two main types of scope:

  1. Global scope: Names in this scope are available to all your code.
  2. Local scope: Names in this scope are only available or visible to the code within the scope.

Scope came about because early programming languages likeBASIC only hadglobal names. With this type of name, any part of the program could modify any variable at any time, making large programs difficult to maintain and debug. To work with global names, you’d need to keep all the code in mind to know what value a given name refers to at any time. This is a major side effect of not having scopes and relying solely on global names.

Modern languages, like Python, use the concept of variable scoping to avoid this kind of issue. When you use a language that implements scopes, you won’t be able to access all the names in a program from all locations. Instead, your ability to access a name depends on its scope.

Note: In this tutorial, you’ll be using the termname to refer to theidentifiers of variables, constants, functions, classes, or any other object that can be assigned a name.

The names in your programs take on the scope of the code block in which you define them. When you can access a name from somewhere in your code, then the name isin scope. If you can’t access the name, then the name isout of scope.

Names and Scopes in Python

Because Python is adynamically-typed language, its variables come into existence when you first assign them a value. Similarly, functions and classes are available after you define them usingdef orclass, respectively. Finally,modules exist after you import them into your current scope.

You can create names in Python using any of the following operations:

OperationExample
Assignmentvariable = value
Importimport module orfrom module import name
Function definitiondef func(): pass
Functionargumentfunc(value1, value2,..., valueN)
Class definitionclass DemoClass: pass

These are all ways to assign a value to either a variable, constant, function, class, instance, or module. In each case, you end up with a name that has a specific scope. This scope will depend on where in your code you’ve defined the name at hand.

Note: There’s an important difference betweenassignment operations andreference or access operations. When you assign a name, you’re either creating that name or making it reference a different object. When you reference a name, you’re retrieving the value that the name points to.

Python uses the location of a name definition to associate it with a particular scope. In other words, the place in which you define a name in your code determines the scope or visibility of that name.

Note: If you come from a language like C, C++, or Java, you might expect that any indented block creates its own scope. In Python, that’s not the case. Onlyfunctions,classes, andmodules introduce a new scope.

Code blocks likeif statements,for andwhile loops, andwith statements don’t create a new scope. So, the names you define inside these blocks remain accessible in the enclosing function or module scope once the block finishes executing.

For example, if you define a name inside a function, then that name will have alocal scope. You can only access the name locally within the function implementation. In contrast, if you define a name at the top level of a module, then that name will have aglobal scope. You’ll be able to access it from anywhere in your code.

Scope vs Namespace in Python

The concept of scope is closely related to the concept ofnamespace. A scope determines thevisibility andlifetime of names, while a namespace provides theplace where those names are stored.

Python implements namespaces asdictionaries that map names to objects. These dictionaries are the underlying mechanism that Python uses to store names under a specific scope. You can often access them through the.__dict__ attribute of the owning object.

Note: To learn more about namespaces, check out theNamespaces in Python tutorial.

For example, names that you define in aREPL session are stored in__main__.__dict__. Here’s an example:

Python
>>>name="Pythonista">>>dir()[    '__builtins__',    '__doc__',    '__loader__',    ...    'name']>>>import__main__>>>__main__.__dict__{    '__name__': '__main__',    '__doc__': None,    ...    'name': 'Pythonista',    '__main__': <module '.../lib/python3.13/_pyrepl/__main__.py'>}

Without arguments, the built-indir() function returns aPython list of names from the current scope, sorted in alphabetical order. These names are the keys of the__main__ module’s.__dict__ attribute.

You can access all names defined at the top level of yourinteractive session directly or from within functions and classes defined there as well:

Python
>>>name'Pythonista'>>>deffunc():...print(name)...>>>func()Pythonista

Here, theglobal variablename is visible and accessible at the top level, as well as inside a function.

In summary, the module’s.__dict__ attribute provides a container for the names and their values, defining the scope of those names. In other words, the names in__main__.__dict__ have a global scope that makes them accessible from anywhere in the interactive session.

The global scope works similarly when you run your code as a Python script or an executable program.

Resolving Names in Python: The LEGB Rule

Python resolves names using what’s known as theLEGB rule, which defines the order in which the interpreter searches through distinct scopes. The letters in this acronym stand forLocal,Enclosing,Global, andBuilt-in. They’re the four levels of scope that you can find in Python. Here’s what these levels represent:

  • Local scope is the body of any Python function orlambda expression. This scope contains the names that you define inside the function. These names are only visible from within the function. Python creates a local scope when you call a function, so you’ll have as many different local scopes as function calls. This is true even if you call the same function multiple times, orrecursively. Each call creates a new local scope.

  • Enclosing scope is a scope that exists only for nested functions and is defined by the outer or enclosing function. This scope contains the names that you define within the enclosing function. The names in the enclosing scope are visible from the code of the inner and outer functions.

  • Global scope is the topmost scope in a Python program or interactive session. This scope contains all of the names that you define at the top level of a script or module. Names in this scope are visible from everywhere in your code.

  • Built-in scope is a special scope that Python creates or loads whenever yourun a script or open an interactive session. This scope contains names such asbuilt-in functions,exceptions, and other attributes that are built into Python. Names in this scope are also available from everywhere in your code.

The LEGB rule defines the order in which Python looks for names. For example, when you reference a given name, Python looks for that name sequentially in the local, enclosing, global, and built-in scope levels if they all exist. If Python finds the name, then you’ll get its first or innermost occurrence. Otherwise, you’ll get aNameError exception.

Here’s a diagram that can help you understand the name lookup process:

Diagram of Local, Enclosed, Global, and Built-in Scopes
The LEGB Rule in Python

Python searches from L to B, looking for the name in question. It’ll return the first instance it finds. This means that if a given name exists in both the local and the global scope, then you’ll get the value associated with the name in the local scope.

When you use nested functions, Python resolves names by first checking the local scope or the innermost function’s local scope. Then, it looks at the enclosing scope. If no match is found, then Python looks at the global and built-in scopes. If it can’t find the name, then it raises an exception.

At any given time during execution, you’ll have at most four active scope levels—local, enclosing, global, and built-in—depending on where you are in the code. You’ll always have at least two active scopes: the global and built-in ones. These two scopes will always be available for you.

The Local Scope

Python creates alocal scope for every function call. The local scope for a given function call will exist until the function returns.

The arguments that you pass to a function and the variables that you define inside a function exist only within the function’s local scope. These names are visible only from within the function. When the function returns, this local scope is destroyed, and the names are forgotten.

Consider the following example that shows how you can’t access names defined inside a function:

Python
>>>defsquare(base):...result=base**2...print(f"The square of{base} is:{result}")...>>>square(10)The square of 10 is: 100>>>square(20)The square of 20 is: 400>>>result# Isn't accessible from outside square()Traceback (most recent call last):...NameError:name 'result' is not defined>>>base# Isn't accessible from outside square()Traceback (most recent call last):...NameError:name 'base' is not defined

Thesquare() function computes the square of a given number,base. When you call the function, Python creates a local scope containing the namesbase—an argument—andresult—a local variable.

After the first call tosquare(),base holds a value of10, andresult has a value of100. The second time, the local names won’t remember the values that were stored in them the first time the function was called. Notice thatbase now holds the value20, andresult holds400.

If you try to accessresult orbase after the function call, then you get aNameError exception, because these names only exist in the local scope created for each call tosquare(). When the function returns, these names are removed.

Because you can’t access local names from outside the function, different functions can define objects with the same name. Check out this example:

Python
>>>defcube(base):...result=base**3...print(f"The cube of{base} is:{result}")...>>>cube(30)The cube of 30 is: 27000

In this example, you definecube() using the same variable and parameter names that you used insquare(). However, sincecube() can’t see the names inside the local scope ofsquare(), and the other way around, both functions work as expected without any name collisions.

The existence of a local scope for functions helps you avoid name collisions in your programs. It also makes functions self-contained and maintainable program units. Additionally, since you can’t change local names from arbitrary places in your code, your programs will be easier to debug, read, and modify.

You can inspect the names in a function’s local scope using the built-indir() andvars() functions:

Python
>>>defcube(base):...result=base**3...print(dir())...print(vars())...print(f"The cube of{base} is:{result}")...>>>cube(5)['base', 'result']{'base': 5, 'result': 125}The cube of 5 is: 125

Again,dir() returns a list of variable names. In contrast,vars() returns a dictionary holding the variable names and the associated values.

From outside the function, you can use the.__code__ attribute to check the names:

Python
>>>square.__code__.co_varnames('base', 'result')>>>square.__code__.co_argcount1>>>square.__code__.co_consts(None, 2, 'The square of ', ' is: ')>>>square.__code__.co_name'square'

In this code example, you inspect.__code__ onsquare(). This is a special attribute that holds information about the code of a Python function. In this case, you see that.co_varnames holds a tuple containing the names that you define insidesquare(). The.co_argcount holds an integer that represents the number of arguments the function takes.

The Enclosing Scope

Theenclosing or nonlocal scope arises when younest a function inside another function. This scope wasadded in Python 2.2 and refers to the namespace of the outer (enclosing) function. The enclosing scope is accessible to the nested functions during their execution.

Names that you define in the enclosing scope are known asnonlocal names because they’re neither local nor global. They’re visible from both the outer and inner functions. Consider the following function:

Python
>>>defouter_func():...# The code block of outer_func() defines its local scope...# It also defines the enclosing scope of inner_func()...variable=100# Local to outer_func() and nonlocal to inner_func()...definner_func():...# The code block of inner_func() defines its local scope...print(f"Printing variable from inner_func():{variable}")......inner_func()...print(f"Printing variable from outer_func():{variable}")...>>>outer_func()Printing variable from inner_func(): 100Printing variable from outer_func(): 100>>>inner_func()Traceback (most recent call last):...NameError:name 'inner_func' is not defined

When you callouter_func(), Python creates a local scope. The local scope ofouter_func() is also the enclosing scope ofinner_func(). From insideinner_func(), this scope is neither the global scope nor the local scope. Instead, it’s a special scope that lies in between those two scopes and is known as the enclosing scope.

Theinner_func() function comes to life only during the execution of its enclosing function,outer_func(). Therefore,inner_func() is only visible to the code inouter_func() that follows the definition ofinner_func(). It’s not visible from the outside, like any other local name.

All the names you create in the enclosing scope are visible from insideinner_func(), except for those created after you callinner_func(). Here’s a modified version ofouter_func() that illustrates this point:

Python
>>>defouter_func():...variable=100...definner_func():...print(f"Printing variable from inner_func():{variable}")...print(f"Printing another_var from inner_func():{another_var}")......inner_func()...another_var=200# This variable is defined after calling inner_func()...print(f"Printing variable from outer_func():{variable}")...>>>outer_func()Printing variable from inner_func(): 100Traceback (most recent call last):...NameError:cannot access free variable 'another_var' where⮑ it is not associated with a value in enclosing scope

When you callouter_func(), the code runs down to the point at which you callinner_func(). The last statement ofinner_func() tries to accessanother_var. However, at this point,another_var isn’t defined yet, so Python raises aNameError exception because it can’t find the name that you’re trying to use.

Last but not least, you can’t modify names in the enclosing scope from inside a nested function unless you use thenonlocal statement in the nested function.

The Global Scope

From the moment you launch a Python program, you’re in the global scope. Internally, Python turns your program’s main script into a module called__main__ to hold the main program’s execution. The namespace of this module represents theglobal scope of your program.

Note: In Python, the notions of global scope and global names are tightly associated with module files. For example, if you define a name at the top level of any Python module, then that name is considered global to the module. That’s the reason why this kind of scope is also calledmodule scope.

If you’re working in a Python interactive session, then you’ll notice that'__main__' is also the name of its main module. To check that out, open an interactive session and access the__name__ variable as shown below:

Python
>>>__name__'__main__'

Whenever you run a Python program or an interactive session, the interpreter executes the code in the module or script that serves as the entry point to your program. This module or script is loaded with the special name,__main__. From this point on, the scope of__main__ is the program’s global scope.

Again, to inspect the names within your global scope, you can usedir(). If you calldir() without arguments, then you’ll get the list of names that live in your current global scope. Take a look at this code:

Python
>>>dir()[    '__builtins__',    '__doc__',    '__loader__',    ...]>>>var=100>>>dir()[    '__builtins__',    '__doc__',    '__loader__',    ...    'var']

When you calldir() with no arguments, you get the list of names available in your current global scope. Note that if you assign a new name—likevar—at the top level of the module, then that name will be added to the list.

The global scope remains in existence for the duration of a program’s execution, and all names defined there are accessible throughout that time. However, when the program terminates, the global scope is discarded, and its names are forgotten. If you want the computed values to persist between runs, then you’ll need to store them externally, such as in a file or database, and reload them the next time the program runs.

You can access or reference the value of any global name from any place in your code. This includes functions and classes. Here’s an example that clarifies these points:

Python
>>>number=42>>>defget_number():...returnnumber# You can access 'number' from inside get_number()...>>>get_number()42>>>number# Remains unchanged42

Insideget_number(), you can access the value ofnumber. This has no effect on your global variablenumber. On the other hand, you can’t assign global names inside functions unless you explicitly declare them as global names using aglobal statement.

Inside a function, if you assign a value to a variable that exists in the global scope, then Python creates a new local variable with the same name instead of modifying the global variable. If you follow this logic, then you’ll realize that the following code won’t work as expected:

Python
>>>number=42>>>defupdate_number():...number=21# Try to update a global variable...returnnumber...>>>update_number()21>>>number42

Withinupdate_number(), you try to change the global variablenumber. However, Python creates a new local variable with the same name,number, inside the function. Note that the localnumber holds21, while the globalnumber still holds42.

Note: You can update or modify global names from any place within your current global scope. To update them from the local scope, you can use theglobal statement.

However, modifying global names is generally considered bad programming practice because it can lead to code that may have some of the following issues:

  • Difficult to debug: You can change the value of a global name from almost any top-level code in a program.
  • Hard to understand: You need to be aware of all the statements that modify global names.
  • Impossible to reuse: You may not be able to reuse the code because it’s dependent on global names that are specific to a concrete program.

Good programming practice recommends using local names rather than global names. Here are some tips:

  • Writeself-contained functions that rely onarguments andlocal names rather than global ones.
  • Aim to useunique names, regardless of the scope.
  • Avoidmodifying global names throughout your programs.
  • Avoidcross-module name modifications.
  • Use global names asconstants that don’t change during your program’s execution.

Up to this point, you’ve covered three different levels of scope in Python. Check out the following example for a summary of where they’re located in your code and how Python looks up names through them:

Python
>>>number=42>>>defouter_func():...definner_func():...print(number)...inner_func()...>>>outer_func()42

When you callouter_func(), you get42 printed on your screen. How does Python look up the namenumber in this example? Following the LEGB rule, it’ll look upnumber in the following places:

  1. Insideinner_func(): This is the local scope, butnumber doesn’t exist there.
  2. Insideouter_func(): This is the enclosing scope, butnumber isn’t defined there either.
  3. At the top level: This is the global scope, andnumber is there, so it gets printed to the screen.

Ifnumber weren’t defined in the global scope, then Python would search the built-in scope. This is the last component of the LEGB rule sequence.

The Built-in Scope

Thebuilt-in scope is a special scope that’s implemented as a standard library module namedbuiltins. All of Python’s built-in objects live in this module. Python automatically loads these objects when you run the Python interpreter. Python searchesbuiltins last in its LEGB lookup sequence.

The names inbuiltins are loaded into your global scope as a mapping with the special name__builtins__, as you can see in the following code:

Python
>>>dir()[    '__builtins__',    ...]>>>list(__builtins__.__dict__.keys())[    '__name__',    '__doc__',    '__package__'    ...    'abs',    'all',    'any',    ...    'BaseException',    'BaseExceptionGroup',    'Exception',    ...]

In the output of the call todir(), you can see that__builtins__ is present in the global scope. If you list the keys of__builtins__.__dict__ itself, then you’ll get the whole list of Python built-in objects, including functions, exceptions, and more.

Even though you can access all of Python’s built-in objects without importing anything, you can also explicitly importbuiltins and access the names usingfully qualified names:

Python
>>>importbuiltins>>>dir(builtins)[    'ArithmeticError',    'AssertionError',    'AttributeError',    ...,    'type',    'vars',    'zip']>>>builtins.sum([1,2,3,4,5])15>>>builtins.max([1,5,8,7,3])8>>>builtins.sorted([1,5,8,7,3])[1, 3, 5, 7, 8]>>>builtins.pow(10,2)100

You can importbuiltins as you would any other Python module. From this point on, you can access all the names inbuiltins by using the dotted attribute lookup or fully qualified names. This technique can be useful when you want to avoid name collisions if any of your global names override built-in names.

You can override or redefine any built-in name in your global scope. If you do so, then keep in mind that this will affect all your code. Take a look at the following example:

Python
>>>abs(-15)15>>>abs=20>>>abs(-15)Traceback (most recent call last):...TypeError:'int' object is not callable

If you define a variable namedabs, then the original built-inabs() function is affected throughout your code. Now, suppose you need to call the originalabs() and you forget that you reassigned that name. In this case, when you callabs() again, you’d get aTypeError becauseabs now holds a reference to an integer, which isn’tcallable.

Note: Accidentally overriding or redefining built-in names in the global scope can lead to bugs and unexpected behavior, especially for other Python developers reading or using your code. To avoid these issues, it’s best to avoid shadowing built-in names.

If you’re experimenting with some code and you accidentally reassign a built-in name during an interactive session, then you can either restart the session or rundel name to remove the redefinition from your global scope. If you revisit the example ofabs(), then you can do something like this:

Python
>>>delabs# Remove the redefined 'abs' from your global scope>>>abs(-15)# Back to the original built-in abs() function15

When youdelete the customabs name, you’re removing the name from your global scope. This allows you to access the originalabs() from the built-in scope again.

To handle this kind of situation, you can take advantage of explicitly importingbuiltins:

Python
>>>importbuiltins>>>abs(-15)15>>>abs=20>>>abs(-15)Traceback (most recent call last):...TypeError:'int' object is not callable>>>builtins.abs(-15)15

Once you explicitly importbuiltins, you can use fully qualified names to unambiguously get the names you need frombuiltins, just like you did withbuiltins.abs() in the example above.

As a quick summary, some of the implications of scope are shown in the following table:

ActionGlobal CodeLocal CodeNested Function Code
Access names in the global scopeYesYesYes
Modify names in the global scopeYesNo (unless declaredglobal)No (unless declaredglobal)
Access names in the local scopeNoYes (in its own local scope), No (in another local scope)Yes (in its own local scope), No (in another local scope)
Override names in the built-in scopeYesYes (during function execution)Yes (during function execution)
Access names in their enclosing scopeN/AYesYes
Modify names in their enclosing scopeN/ANo (unless declaredglobal)No (unless declarednonlocal)

Additionally, code in different scope levels can use the same name for different objects. For example, you can use a local variable namedvalue and also a global variable with the same name,value. However, reusing names across scopes can lead to confusion or unexpected behavior.

Modifying the Behavior of a Python Scope

So far, you’ve learned how Python scope works and restricts the visibility of variables, functions, classes, and other Python objects to certain parts of your code. You now know that you can access or reference global names from anywhere in your code, but they can be modified or updated from within the local scope.

You also know that you can access local names only from inside the local scope where they were created or from within nested functions. You can’t access them from the global scope or from other local scopes. Additionally, you’ve learned thatnonlocal names are accessible from inside nested functions, but you can’t modify or update them directly from there.

Even though Python follows these general rules by default, there are ways to modify this behavior. Python provides two keywords that allow you to modify the content of global and nonlocal names:

  1. global
  2. nonlocal

In the next two sections, you’ll learn how to use these keywords to modify the default behavior of name scope in Python.

Theglobal Statement

You already know that when you try to assign a value to a global variable inside a function, you create a new local variable in the function’s local scope. You can modify this behavior by using theglobal statement. With this statement, you can set a series of names to treat them as global names.

Theglobal statement consists of theglobal keyword followed by one or more names separated by commas. You can also use multipleglobal statements with a name or a list of names. All the names that you list in aglobal statement will be mapped to the global scope.

Here’s an example where you try to update a global variable from within a function:

Python
>>>counter=0# A global variable>>>defupdate_counter():...counter=counter+1# Fails trying to update 'counter'...>>>update_counter()Traceback (most recent call last):...UnboundLocalError:cannot access local variable 'counter' where⮑ it is not associated with a value

Insideupdate_counter(), you try to update the globalcounter by using its previous value,0. However, Python assumes that thecounter name is local toupdate_counter() and raises anUnboundLocalError exception because the name isn’t defined yet, but the code is trying to reuse a previous value.

If you want this code to work the way you expect, then you can use theglobal statement as shown below:

Python
>>>counter=0# A global variable>>>defupdate_counter():...globalcounter# Declares 'counter' as a global variable...counter=counter+1# Successfully updates 'counter'...>>>update_counter()>>>counter1>>>update_counter()>>>counter2>>>update_counter()>>>counter3

In this new version ofupdate_counter(), you add the statementglobal counter to the body of the function right before you try to changecounter. With this tiny change, you map the namecounter in the function’s local scope to the same name in the global scope. From this point on, you can freely modifycounter insideupdate_counter(), and all changes will affect the global variable instead of creating a new local one.

With the statementglobal counter, you tell Python to look in the global scope for the namecounter. This way, the assignmentcounter = counter + 1 doesn’t create a new name in the function’s local scope, but updates it in the global scope.

Note: In general, usingglobal is considered bad practice. If you find yourself usingglobal to fix problems like the one above, stop and consider whether there’s a better way to write your code.

For example, you can write a self-contained function that relies on local names rather than global names:

Python
>>>global_counter=0# A global variable>>>defupdate_counter(counter):...returncounter+1# Relies on a local variable 'counter'...>>>global_counter=update_counter(global_counter)>>>global_counter1>>>global_counter=update_counter(global_counter)>>>global_counter2>>>global_counter=update_counter(global_counter)>>>global_counter3

This implementation ofupdate_counter() definescounter as a parameter and returns its value increased by1 every time you call the function. This way, the result ofupdate_counter() depends solely on thecounter value you pass in, not on any changes to the global variableglobal_counter.

You can also use aglobal statement to create global nameslazily. Take a look at the following code:

Python
>>>number# The global variable 'number' doesn't existTraceback (most recent call last):...NameError:name 'number' is not defined>>>defcreate_lazy_name():...globalnumber# Declare the variable 'number' as global...number=42# Create a global variable lazily...returnnumber...>>>create_lazy_name()42>>>number# The variable 'number' is now available in the global scope42>>>dir()[    '__builtins__',    ...    'number']

When you callcreate_lazy_name(), it creates a global variable callednumber, which didn’t exist before. Note that after calling the function, thenumber is available in the global scope.

Note: Even though you can use aglobal statement to create global names lazily, this can be a dangerous practice that can lead to buggy code. So, it’s best to avoid it in your code.

For example, suppose you’re trying to access one of those lazy names and, for some reason, your code hasn’t called the function that creates that name yet. In this case, you’ll get aNameError exception, and your program will crash.

Finally, it’s worth noting that you can useglobal from inside any function or nested function, and the names listed will always be mapped to names in the global scope.

Finally, even though using aglobal statement at the top level of a module is legal, it doesn’t make much sense because any name assigned in the global scope is already a global name by definition:

Python
>>>number=42>>>dir()[    '__builtins__',    ...    'number']>>>globalnumber>>>dir()[    '__builtins__',    ...    'number']

Usingglobal like in this example doesn’t change anything in your current global scope, as you can see when you compare the outputs ofdir(). The variablenumber is global, whether you use theglobal statement or not.

Thenonlocal Statement

Like global names, nonlocal names can be accessed from inner functions, but not assigned or updated. If you want to modify them, then you need to use thenonlocal statement. With this statement, you can define a series of names that are going to be treated as nonlocal.

Thenonlocal statement consists of thenonlocal keyword followed by one or more names separated by commas. These names refer to the same names in the enclosing scope. The following example shows how you can usenonlocal to modify a variable defined in the enclosing or nonlocal scope:

Python
>>>deffunction():...number=42# A nonlocal variable...defnested():...nonlocalnumber# Declare 'number' as nonlocal...number+=42...nested()...print(number)...>>>function()84

With the statementnonlocal number, you tell Python that you’ll be modifyingnumber insidenested(). Then, you incrementnumber using anaugmented assignment operation. This change is reflected in the nonlocal namenumber, which now has a value of84.

Unlikeglobal, you can’t usenonlocal outside of a nested or inner function. To be more precise, you can’t use anonlocal statement in either the global scope or in a local scope. Here’s an example:

Python
>>>nonlocalvariable# Try to use nonlocal in the global scope  File"<stdin>", line1SyntaxError:nonlocal declaration not allowed at module level>>>deffunc():...nonlocalvariable# Try to use nonlocal in a local scope...print(variable)...  File"<stdin>", line2SyntaxError:no binding for nonlocal 'variable' found

Here, you first try to use anonlocal statement in the global scope. Sincenonlocal only works inside an inner or nested function, you get aSyntaxError telling you that you can’t usenonlocal in a module scope. Notice thatnonlocal doesn’t work inside a local scope either.

Unlikeglobal, you can’t usenonlocal to create nonlocal names lazily. Names must already exist in the enclosing scope if you want to use them as nonlocal names:

Python
>>>deffunction():...defnested():...nonlocalvariable# Try to declare a nonlocal variable lazily...  File"<stdin>", line3SyntaxError:no binding for nonlocal 'variable' found

In this example, when you try to define a nonlocal name usingnonlocal variable, Python immediately raises aSyntaxError becausevariable doesn’t exist in the enclosing scope ofnested().

Using the Enclosing Scope to Create Closures

When you handle a nested function as data, the statements that make up that function are packaged together with the environment in which they execute. The resulting object is known as aclosure. In other words, a closure is a callable that carries information about its enclosing scope, even though that scope has completed its execution.

Note: To learn more about closures, check out thePython Closures: Common Use Cases and Examples tutorial.

Closures provide a way to retain state information between function calls. Take a look at the following example:

Python
>>>defpower_factory(exponent):...defpower(base):...returnbase**exponent...returnpower...>>>square=power_factory(2)>>>square(10)100>>>cube=power_factory(3)>>>cube(10)1000>>>cube(5)125>>>square(15)225

Your closure functionpower_factory() takes an argument calledexponent. You can use this function to build closures that run different power operations. This works because each call topower_factory() gets its own set of state information. In other words, it gets its value forexponent.

Note: Variables likeexponent that are referenced within a code block but not defined there are known asfree variables. These variables derive their values from the surrounding scope. Free variables are essential to how closures function, as they allow a function to “remember” and retain access to external variables, even after the enclosing scope has finished executing. This mechanism enables closures to preserve state between successive calls.

In the example above, the inner functionpower() is first assigned tosquare. In this case, the function remembers thatexponent equals2. In the second example, you callpower_factory() using3 as an argument. This way,cube holds a function object, which remembers thatexponent is3. Notice that you can reusesquare andcube because they don’t forget their respective state information.

Bringing Names to Scope Withimport

When you write a Python program, you can organize the code into severalmodules. For your program to work, you need a mechanism that allows you to use the content of those modules in your main program,__main__. Python’simport system allows you to do exactly that.

The import system allows you to load the names defined in a module into your current global scope. Take a look at the following code as an example of what happens when you import some standard modules and names:

Python
>>>dir()[__builtins__', ...]>>>importsys>>>dir()[    '__builtins__',    ...    'sys']>>>importos>>>dir()[    '__builtins__',    ...    'os',    'sys']>>>fromfunctoolsimportpartial>>>dir()[    '__builtins__',    ...    'os',    'partial',    'sys']

In this example, you import thesys andos modules from the Pythonstandard library. By callingdir() with no arguments, you can see that these modules are now available for you as names in your current global scope. You can usedot notation to access the names that are defined insys andos.

Note: If you want to dive deeper into how imports work in Python, then check out thePython import: Advanced Techniques and Tips tutorial.

In the last example, you use the formfrom module import name, which allows you to import a name directly. In other words, you don’t need to explicitly use dot notation and fully qualified names in this case.

Exploring Other Scope Behaviors in Python

Some Python structures handle name resolution in ways that don’t fully align with the LEGB rule. These structures include:

In the next few sections, you’ll cover how Python scope works in each of these cases. With this knowledge, you’ll be able to avoid subtle errors related to name scope in these Python structures.

The Scope of Comprehension Variables

The first structure you’ll learn about is acomprehension. Comprehensions offer a compact way to process all or part of the elements in acollection. You can use comprehensions to createlists,dictionaries, andsets.

Comprehensions consist of a pair of brackets ([]) or curly braces ({}) containing an expression, followed by one or morefor clauses, each of which can optionally have anif clause.

Thefor clause in a comprehension works similarly to a traditionalfor loop. The loop variable in a comprehension is local to the comprehension itself:

Python
>>>[number**2fornumberinrange(5)][0, 1, 4, 9, 16]>>>number# Try to access the comprehension variableTraceback (most recent call last):...NameError:name 'number' is not defined

Once you run this list comprehension, the variablenumber is forgotten, and you can’t access its value anymore. It’s unlikely that you need to use this variable outside of the comprehension, but regardless, Python makes sure that its value is no longer available once the comprehension finishes. So, the scope of this variable is limited to the comprehension itself.

Note that this applies only to comprehensions. In regularfor loops, the loop variable holds the last value processed by the loop:

Python
>>>foriteminrange(5):...print(item)...01234>>>item# Access the loop variable4

You can access the loop variableitem once the loop has finished. Here, the loop variable holds the last value processed by the loop, which is4 in this example.

The Scope of Exception Variables

Another atypical scope is related to the optional variable in anexcept clause. The exception variable is a variable that holds a reference to anyexception raised in atry statement. Such a variable is local to theexcept block and is forgotten when the block ends:

Python
>>>numbers=[1,2,3]>>>try:...numbers[4]...exceptIndexErroraserror:...# The variable 'error' is local to this block...error...IndexError('list index out of range')>>>errorTraceback (most recent call last):...NameError:name 'error' is not defined

Theerror variable holds a reference to the exception raised in thetry block. You can only useerror inside theexcept code block. So, the exception variable is local to theexcept block. When you try to accesserror from outside theexcept block, you’ll get aNameError exception.

To work around this behavior, you can use an auxiliary variable:

Python
>>>numbers=[1,2,3]>>>exception=None>>>try:...numbers[4]...exceptIndexErroraserror:...# The variable error is local to this block...exception=error...error...IndexError('list index out of range')>>>exceptionIndexError('list index out of range')

You useexception as an auxiliary variable to hold a reference to the exception raised in thetry clause. This can be useful when you need to do something with the exception object once the code block has finished. Note that if no exception is raised, thenexception remainsNone.

The Scope of Class and Instance Attributes

When you define a class, you’re creating a new namespace. Names assigned at the top level of a class live in its.__dict__ attribute:

Python
>>>classA:...attr=100...>>>A.__dict__.keys()dict_keys(    [        '__module__',        '__firstlineno__',        'attr',        '__static_attributes__',        ...    ])

When you inspect the keys of.__dict__, you’ll notice that"attr" is in the list along with other names. This dictionary represents the class namespace. The names in this space are visible to all instances of the class and to the class itself. This fact defines their scope.

To access a class attribute from outside the class, you need to use the class name:

Python
>>>classA:...attr=100...print(attr)# Access a class attribute directly...100>>>A.attr# Access a class attribute from outside the class100>>>attr# The attribute isn't defined outside ATraceback (most recent call last):...NameError:name 'attr' is not defined

InsideA, you can access the class attributes directly, just like you did in the statementprint(attr). To access any class attribute once the code block of the class has been executed, you need to use dot notation or anattribute reference, as you did withA.attr. You’ll get aNameError exception if you try to access.attr without referencing the class.

Inclass inheritance scenarios—when one class extends another—attribute resolution follows themethod resolution order (MRO). So, subclasses may inherit and access attributes defined in their parent classes, unless explicitly overridden.

Note: To learn more about classes, check out thePython Classes: The Power of Object-Oriented Programming tutorial.

You can also access any class attribute using an instance of the class:

Python
>>>a=A()>>>a.attr100

Once you have an instance of a class, you can access its attributes using dot notation, as you did here witha.attr.

Class attributes are specific to the class object, but you can access them from any instance of the class. It’s worth noting that class attributes are common to all instances of a class. If you modify a class attribute in one instance, then the changes will be visible in other instances.

Whenever you call a class, you create a new instance of that class. Instances have their own.__dict__ attribute that holds the names associated with the instance itself. These names are commonly called instance attributes and are local and specific to each instance. This means that if you modify an instance attribute, the change will only be visible to that specific instance.

To create, update, or access any instance attribute from inside the class, you need to useself along with the dot notation. Here,self is a special object that represents the current instance. On the other hand, to update or access any instance attribute from outside the class, you need to create an instance and then use the dot notation:

Python
>>>classA:...def__init__(self,var):...self.var=var# Create a new instance attribute...self.var*=2# Update the instance attribute...>>>a=A(100)>>>a.var200>>>A.varTraceback (most recent call last):...AttributeError:type object 'A' has no attribute 'var'

TheA class takes an argument calledvar, which is automatically doubled inside.__init__() using the assignment operationself.var *= 2. You can access the.var attribute through thea instance. However, it’s not possible to access instance attributes through the class.

Note: For more on how classes work in Python, check out theObject-Oriented Programming (OOP) in Python tutorial.

In summary, to access class attributes, you need to use the class itself or one of its instances. To access instance attributes, you need to use a concrete instance. These rules define the scope of class and instance attributes.

Using Scope-Related Built-in Functions

There are two Python built-in functions that are closely related to namespaces and, therefore, to the concept of scope:

  1. globals()
  2. locals()

In the following sections, you’ll learn the basics about these functions and how to use them in your Python code.

Theglobals() Function

The built-inglobals() function returns a namespace dictionary with all the names—and associated objects—that are currently in your global scope. Here’s an example of calling the function at the top level of aREPL session:

Python
>>>globals(){    '__name__': '__main__',    '__doc__': None,    '__package__': None,    ...}>>>number=42>>>globals(){    '__name__': '__main__',    '__doc__': None,    '__package__': None,    ...    'number': 42}

The first call toglobals() returns a dictionary containing the names in your__main__ program. Note that when you assign a new name at the top level of__main__, then the name is added to the dictionary thatglobals() returns.

A quick example of how you can useglobals() in your code would be to dynamically dispatch functions depending on your current platform. Consider the following toy example:

Pythondispatch.py
fromsysimportplatformdeflinux_print():print("Printing from Linux...")defwin32_print():print("Printing from Windows...")defdarwin_print():print("Printing from macOS...")printer=globals()[platform+"_print"]printer()

If yourun this script from the command line, the output will depend on your current platform, which is a cool use case forglobals().

Note that you can use theglobals() dictionary just like you would use any regular dictionary. For example, you caniterate through it using the standard methods like.keys(),.values(), and.items(). You can also perform key lookups onglobals() using square brackets, as inglobals()["name"].

You can even modify the content ofglobals(). Take a look at this example:

Python
>>>globals()["__doc__"]="""Docstring for __main__""">>>__doc__'Docstring for __main__'

Here, you change the value associated with the"__doc__" key to add adocstring for__main__. This way, the main module’s docstring will have the value'Docstring for __main__'.

Thelocals() Function

The built-inlocals() function returns a dictionary that holds a copy of the current state of the local namespace. When you calllocals() in a function block, you get all the names assigned in the local scope down to the point where you calllocals().

Here’s a quick toy example:

Python
>>>deffunction(arg):...var=100...print(locals())...another=200...>>>function(300){'var': 100, 'arg': 300}

Whenever you calllocals() insidefunction(), the resulting dictionary contains the namevar mapped to the value100, andarg mapped to300. Becauselocals() only grabs the names assigned before you call it, theanother variable isn’t in the dictionary yet.

If you calllocals() in the global scope, then you’ll get the same dictionary that you would get if you were to callglobals():

Python
>>>locals(){    '__name__': '__main__',    '__doc__': None,    '__package__': None,    ...}>>>locals()isglobals()True

When you calllocals() in the global scope, you get a dictionary that’s identical to the dictionary returned byglobals().

Note that you can modify the content oflocals(), but the changes won’t have an effect on the values of local names:

Python
>>>deffunction():...var=100...locals()["var"]=200...print(var)...>>>function()100

In this example, you modify the content ofvar usinglocals(), but the change doesn’t affect the value ofvar becauselocals() returns ashallow copy of the local scope.

Conclusion

You’ve delved into the concept of scope in Python, learning how variable visibility and lifetime are managed within your code. You explored the LEGB rule, which stands forlocal,enclosing,global, andbuilt-in, and defines how Python resolves variable names across different scope levels.

Additionally, you learned how to modify scope behavior with theglobal andnonlocal keywords, and how to interact with names in the global and local scopes using the built-in functionsglobals() andlocals().

Grasping the concept of scope is crucial for Python developers, as it helps you avoid common pitfalls such as name collisions and unintended side effects.

In this tutorial, you’ve learned how to:

  • Understand what the termscope means and how it works in Python
  • Apply theLEGB rule to resolve names across different scope levels
  • Modify scope behavior using theglobal andnonlocal statements
  • Use built-in tools likeglobals() andlocals() to interact with names
  • Recognize specific scope behaviors incomprehensions,exception blocks, andclasses

With these skills, you can now harness the power of scope to write more robust and reliable programs and minimize bugs related to name collisions.

Get Your Code:Click here to download the free sample code that you’ll use to learn about Python scope and the LEGB rule.

Frequently Asked Questions

Now that you have some experience with scope and the LEGB rule in Python, you can use the questions and answers below to check your understanding and recap what you’ve learned.

These FAQs are related to the most important concepts you’ve covered in this tutorial. Click theShow/Hide toggle beside each question to reveal the answer.

In Python, the term scope defines where a variable or other name is accessible or visible in your code. It determines whether you can use the variable from a specific part of your code.

A Python namespace is represented by a dictionary that maps names to objects, organizes variables, and determines their scope.

The four scope levels in Python are local, enclosing, global, and built-in. Each of them provides a different level of visibility and accessibility for the contained names.

Common scope-related built-in functions areglobals() andlocals(), which provide access to the global and local namespaces.

Take the Quiz: Test your knowledge with our interactive “Python Scope and the LEGB Rule: Resolving Names in Your Code” quiz. You’ll receive a score upon completion to help you track your learning progress:


Python Scope and the LEGB Rule: Resolving Names in Your Code

Interactive Quiz

Python Scope and the LEGB Rule: Resolving Names in Your Code

Test your Python scope skills with this quiz. Practice the LEGB rule, scope vs namespace, globals vs locals, and cases like comprehensions and exceptions.

Recommended Course

The LEGB Rule & Understanding Python Scope(33m)

🐍 Python Tricks 💌

Get a short & sweetPython Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

Python Tricks Dictionary Merge

AboutLeodanis Pozo Ramos

Leodanis is a self-taught Python developer, educator, and technical writer with over 10 years of experience.

» More about Leodanis

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

MasterReal-World Python Skills With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

MasterReal-World Python Skills
With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

What Do You Think?

Rate this article:

What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.

Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students.Get tips for asking good questions andget answers to common questions in our support portal.


Looking for a real-time conversation? Visit theReal Python Community Chat or join the next“Office Hours” Live Q&A Session. Happy Pythoning!

Keep Learning

Related Topics:intermediatepython

Related Learning Paths:

Related Courses:

Related Tutorials:

Keep reading Real Python by creating a free account or signing in:

Already have an account?Sign-In

Almost there! Complete this form and click the button below to gain instant access:

Python Scope and the LEGB Rule: Resolving Names in Your Code

Python Scope and the LEGB Rule: Resolving Names in Your Code

🔒 No spam. We take your privacy seriously.


[8]ページ先頭

©2009-2026 Movatter.jp