TL;DR
In Python programming language, scopes are used to define the accessibility of variables. Python has four types of scopes: Local, Enclosing, Global, and Built-in. Local scopes are used to define variables within a function, while Global scopes define variables outside of any function. Enclosing scopes are used when a function is nested inside another function, while Built-in scopes are pre-defined by the Python interpreter.
Explanation
Python uses scopes and namespaces to manage the names (variables, functions, classes, etc.) used in a program and to ensure that these names can be referenced and used in a consistent and predictable way.
By using scopes and namespaces, Python ensures that names are unique and unambiguous within a given scope, and that names in different scopes can be distinguished from each other. This helps to prevent naming conflicts and makes it easier to understand and maintain Python code. Python has four types of scopes which are accessed in the following order:
LEGB:
Local → Enclosing → Global → Built-in
1. Local Scope (function)
A local scope is created whenever a function is called, and it is destroyed when the function completes execution. Variables defined inside a function have a local scope, which means they can only be accessed within that function. A local variable can have the same name as a global variable, but the local variable will take precedence within the function.
def my_func():
x = 5
print(x)
my_func() # output: 5
In the above example, x
is a local variable that is defined within the function my_func()
. The variable x
can only be accessed within the function, and it is destroyed once the function completes execution.
2. Enclosing Scope (nonlocal)
An enclosing scope is created when a function is nested inside another function. If a variable is not defined in the local scope, Python will search for it in the enclosing scope. An enclosing scope is used to access variables defined in the outer function.
def outer_func():
x = 5
def inner_func():
print(x)
inner_func()
outer_func() # output: 5
In the above example, inner_func()
is nested inside outer_func()
, and x
is defined in the outer function. inner_func()
does not define x
, so Python searches for it in the enclosing scope and finds it in the outer function.
3. Global Scope (module)
A global scope is created when a variable is defined outside of any function. Global variables can be accessed from any function in the program. It is recommended to avoid using global variables whenever possible, as they can lead to unintended consequences and make it difficult to track changes in the program.
x = 5
def my_func():
print(x)
my_func() # output: 5
In the above example, x
is defined in the global scope and can be accessed from any function in the program.
4. Built-in Scope
A built-in scope is pre-defined by the Python interpreter and contains functions and variables that are available to all programs. Built-in functions like print()
and len()
are examples of variables that are defined in the built-in scope.
Modifying the scope
The scope of a name cannot be modified directly, but it can be influenced by the way that functions and classes are defined and by the use of certain keywords.
global keyword
When a variable is declared as global inside a function, its scope is extended to the entire module, allowing it to be accessed and modified from any part of the code. For example:
x = 10
def func():
global x
x = 20
func()
print(x) # Output: 20
nonlocal keyword
When a variable is declared as nonlocal inside a nested function, its scope is extended to include the enclosing function’s scope. This allows the variable to be accessed and modified from both the nested function and the enclosing function
def outer():
x = 10
def inner():
nonlocal x
x = 20
inner()
print(x) # Output: 20
outer()
It’s important to note that modifying the scope of a name should be done with caution, as it can make code more difficult to understand and maintain. It’s generally recommended to use local variables within functions, and to pass arguments and return values between functions to avoid creating unnecessary dependencies on global variables.
Inspecting attributes and variables
g**lobals()**
, locals()
, vars()
and dir()
are four built-in functions that are used to inspect variables and their attributes in different scopes. Here's a brief explanation of each function:
1. globals()
: This function returns a dictionary representing the global namespace.
x = 10
def func():
y = 20
print(globals()['x']) # Output: 10
func()
2. locals()
: This function returns a dictionary representing the local namespace in the current scope.
def func():
x = 10
y = 20
print(locals())
func()
3. vars()
: This function is similar to locals()
, but it can be called on any object to return its attribute dictionary. If called without any argument, it returns the local namespace as a dictionary. Here's an example:
class MyClass:
x = 10
def __init__(self):
self.y = 20
obj = MyClass()
print(vars(obj)) # Output: {'y': 20}
4. dir()
: This function returns a list of valid attributes for a given object, including both built-in and user-defined attributes. If called without any argument, it returns the names in the current local scope. Here's an example:
class MyClass:
x = 10
def __init__(self):
self.y = 20
obj = MyClass()
print(dir(obj))
# Output: ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__',
'__eq__', '__format__', '__ge__', '__getattribute__',
'__gt__', '__hash__', '__init__', '__le__', '__lt__',
'__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
'__weakref__', 'x', 'y']
In this example, dir(obj)
returns a list of all the valid attributes for the obj
instance of MyClass
, including built-in attributes such as __class__
, and user-defined attributes such as x
and y
.
Conclusion
Scopes are an important concept in the Python programming language, as they define the accessibility of variables within a program. Python has four types of scopes: Local, Enclosing, Global, and Built-in. It is recommended to use local variables whenever possible, and to avoid using global variables unless absolutely necessary.