
A variable is only available from inside the region it is created. This is called scope.
A variable created inside a function belongs to the local scope of that function, and can only be used inside that function.
A variable created inside a function is available inside that function:
def myfunc():
x = 300
print(x)
myfunc()As explained in the example above, the variable x is not available outside the function, but it is available for any function inside the function:
The local variable can be accessed from a function within the function:
def myfunc():
x = 300
def myinnerfunc():
print(x)
myinnerfunc()
myfunc()A variable created in the main body of the Python code is a global variable and belongs to the global scope.
Global variables are available from within any scope, global and local.
A variable created outside of a function is global and can be used by anyone:
x = 300
def myfunc():
print(x)
myfunc()
print(x)If you operate with the same variable name inside and outside of a function, Python will treat them as two separate variables, one available in the global scope (outside the function) and one available in the local scope (inside the function):
The function will print the local x, and then the code will print the global x:
x = 300
def myfunc():
x = 200
print(x)
myfunc()
print(x)If you need to create a global variable, but are stuck in the local scope, you can use the global keyword.
The global keyword makes the variable global.
If you use the global keyword, the variable belongs to the global scope:
def myfunc():
global x
x = 300
myfunc()
print(x)Also, use the global keyword if you want to make a change to a global variable inside a function.
To change the value of a global variable inside a function, refer to the variable by using the global keyword:
x = 300
def myfunc():
global x
x = 200
myfunc()
print(x)The nonlocal keyword is used to work with variables inside nested functions.
The nonlocal keyword makes the variable belong to the outer function.
If you use the nonlocal keyword, the variable will belong to the outer function:
def myfunc1():
x = "Jane"
def myfunc2():
nonlocal x
x = "hello"
myfunc2()
return x
print(myfunc1())Python follows the LEGB rule when looking up variable names, and searches for them in this order:
Understanding the LEGB rule:
x = "global"
def outer():
x = "enclosing"
def inner():
x = "local"
print("Inner:", x)
inner()
print("Outer:", x)
outer()
print("Global:", x)Decorators let you add extra behavior to a function, without changing the function's code.
A decorator is a function that takes another function as input and returns a new function.
Define the decorator first, then apply it with @decorator_name above the function.
A basic decorator that uppercases the return value of the decorated function:
def changecase(func):
def myinner():
return func().upper()
return myinner
@changecase
def myfunction():
return "Hello Sally"
print(myfunction())By placing @changecase directly above the function definition, the function myfunction is being "decorated" with the changecase function.
A decorator can be called multiple times. Just place the decorator above the function you want to decorate.
Using the @changecase decorator on two functions:
def changecase(func):
def myinner():
return func().upper()
return myinner
@changecase
def myfunction():
return "Hello Sally"
@changecase
def otherfunction():
return "I am speed!"
print(myfunction())
print(otherfunction())Functions that require arguments can also be decorated, just make sure you pass the arguments to the wrapper function:
Functions with arguments can also be decorated:
def changecase(func):
def myinner(x):
return func(x).upper()
return myinner
@changecase
def myfunction(nam):
return "Hello " + nam
print(myfunction("John"))Sometimes the decorator function has no control over the arguments passed from decorated function, to solve this problem, add (*args, **kwargs) to the wrapper function, this way the wrapper function can accept any number, and any type of arguments, and pass them to the decorated function.
Secure the function with *args and **kwargs arguments:
def changecase(func):
def myinner(*args, **kwargs):
return func(*args, **kwargs).upper()
return myinner
@changecase
def myfunction(nam):
return "Hello " + nam
print(myfunction("John"))Decorators can accept their own arguments by adding another wrapper level.
A decorator factory that takes an argument and transforms the casing based on the argument value:
def changecase(n):
def changecase(func):
def myinner():
if n == 1:
a = func().lower()
else:
a = func().upper()
return a
return myinner
return changecase
@changecase(1)
def myfunction():
return "Hello Linus"
print(myfunction())You can use multiple decorators on one function.
This is done by placing the decorator calls on top of each other.
Decorators are called in the reverse order, starting with the one closest to the function.
One decorator for upper case, and one for adding a greeting:
def changecase(func):
def myinner():
return func().upper()
return myinner
def addgreeting(func):
def myinner():
return "Hello " + func() + " Have a good day!"
return myinner
@changecase
@addgreeting
def myfunction():
return "Tobias"
print(myfunction())Functions in Python has metadata that can be accessed using the __name__ and __doc__ attributes.
Normally, a function's name can be returned with the __name__ attribute:
def myfunction():
return "Have a great day!"
print(myfunction.__name__)