Scope and Decorators
On this page
Understanding Variable ScopeTheglobal KeywordThe nonlocal Keyword and Nested FunctionsFunction DecoratorsDecorators with ArgumentsAs your programs grow larger, understanding where and how variables exist becomes important.
This idea is known as scope — the part of a program where a variable can be seen or used.
Once you understand scope, we’ll move to decorators, a feature that lets you extend or modify the behavior of functions without changing their code.
Understanding Variable Scope
A namespace maps names to objects. Scope determines where Python looks for a name.
We begin with local and global scope, then examine an enclosing function’s scope: - Local scope: variables created inside a function, available only there. - Global scope: variables created outside any function, available throughout the file.
Let’s see a simple example:
message = "Hello from the global scope"
def greet():
local_message = "Hello from inside the function"
print(local_message) # accessible only inside the function
print(message) # accessing the global variable
greet()
print(message) # accessible anywhereOutput
Hello from inside the function
Hello from the global scope
Hello from the global scope
If you try to print local_message outside the function, Python will raise an error because it only exists within greet().
The global Keyword
Sometimes, you may want to modify a global variable inside a function.
To do this, you must declare it as global:
count = 0
def increase_count():
global count
count += 1
print("Inside function:", count)
increase_count()
print("Outside function:", count)Output
Inside function: 1
Outside function: 1
Without the global keyword, Python would assume count is a new local variable and raise an error.
The nonlocal Keyword and Nested Functions
A function can also contain another function.
An inner function can read names from an enclosing function without nonlocal. Use nonlocal when the inner function needs to assign to an existing name in that enclosing function.
def outer():
x = "outer value"
def inner():
nonlocal x
x = "inner value"
print("Inside inner:", x)
inner()
print("Inside outer:", x)
outer()Output
Inside inner: inner value
Inside outer: inner value
Here,nonlocal allows the inner function to modify a variable that belongs to the enclosing function.
Function Decorators
Now that you know about scope, decorators will make more sense.
A decorator is a function that takes another function as input, adds some extra behavior, and returns it. They are often used for logging, access control, timing, or formatting output.
Let’s start with a simple example.
def simple_decorator(func):
def wrapper():
print("Before the function runs")
func()
print("After the function runs")
return wrapperYou can apply the decorator to another function using the @ symbol:
@simple_decorator
def greet():
print("Hello, Python!")
greet()Output
Before the function runs
Hello, Python!
After the function runs
Here’s what happens:
- The
@simple_decoratorline tells Python to pass the functiongreettosimple_decoratorand replacegreetwith the returned wrapper. - The
wrapper()function adds code before and after the original function. - When
greet()is called, it runs inside the decorator.
Decorators with Arguments
You can make decorators more flexible by allowing them to work with any function arguments using *args and **kwargs.
def logger(func):
def wrapper(*args, **kwargs):
print(f"Running {func.__name__} with arguments {args} {kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} finished with result {result}")
return result
return wrapper
@logger
def add(a, b):
return a + b
add(3, 5)Output
Running add with arguments (3, 5) {}
add finished with result 8
This pattern is often used in real-world applications — for example, in web frameworks, decorators can check user permissions, handle errors, or measure performance.