×We're offering Women's Scholarships for applicants to our February class! Details here.
By the end of this chapter, you should be able to:
In Python we have function scope, which prohibits us from accessing variables created inside of a function from outside of that function:
def func(): x = 5 return x func() # 5 x # NameError
The global scope includes all variables defined outside of functions. But if we try to use a global variable in a method, we will see
UnboundLocalError: local variable VARIABLE_NAME referenced before assignment. This happens because a method in Python either has local variables or global variables. If variable is defined anywhere in a method and that variable has the same name as a global variable, then the new local variable will be used in the function instead of the global. But if you actually want to assign a global variable from within a function, you need to use the
global keyword. Using global variables in general is not best practice:
id = 0 def increment_id(): id += 1 increment_id() # UnboundLocalError: local variable 'id' referenced before assignment def increment_id(): global id id += 1 increment_id() # The global id is now 1
In Python you need to explicitly state that a variable should be global, using the
In Python we can display all of the local variables and global variables using the
def print_locals(): x = 2 name = "Elie" print(locals()) name = "person" print(globals()) print(locals())
In Python we do have support for closures, a feature where an inner function has access to variables in an outer function's scope, even after the outer function has finished executing.
def outer(a): def inner(b): return a + b return inner outer(3)(4) # 7 x = outer(2) x(10) # 12
However, closures in Python are "weak" and have some limitations. For example, if you want to change the value of a variable from an outer scope, you'll run in to problems:
def counter(): x = 0; def increment(): x += 1 print(x) return increment counter()() # UnboundLocalError: local variable 'x' referenced before assignment
Again, this is because the
x inside of
increment is a new variable, bound to the scope of
increment. It's not a reference to
x coming from the scope of
We can get around the problem with the example above by setting attributes on the inner function, rather than trying to change variables from an outer scope:
# We can get around this by doing def outer_count(): def inner_count(): inner_count.x += 1 print(inner_count.x) inner_count.x = 0 return inner_count
You can read more about this concept of "read only" closures here.
Something that Python offers us is the ability to add what is called a
docstring. Let's see what that looks like
def say_hello(): # we are using three quotes so that this can be a multi-line string if necessary """This function returns the string hello when called""" return "hello"
We can call this function using
say_hello() # "hello" say_hello.__doc__ # "This function returns the string hello when called" help(say_hello) # gives us even more detail with the docstring!
Docstrings are essential when writing methods and can be thought of like an enhanced comment. Docstrings are also very useful when writing tests, as you can see what the docstring is when running the test. You are highly encouraged to write docstrings for your functions, and inside classes as well. You can read more about standards on docstrings here.
Unlike languages like Java and C++, Python is a dynamically typed language. This means that we do not need to explicitly define the data type of a variable when initializing it. This gives us a bit more flexibility around our code, but sometimes we want to clearly indicate that a certain data type is what should be passed as a parameter, or that a function returns a specific value. We can do that in Python! Let's see what that looks like:
def add(a: int, b: int) -> int: """This function returns the sum of two numbers""" return a + b
We are specifying that both
ints and the return value from the function is an
int as well. We can also combine this with default parameter values!
def add(a: int = 5 ,b: int = 5) -> int: """This function returns the sum of two numbers with default values of 5 for a and 5 for b""" return a + b
When you're ready, move on to Functions Exercises