What is a function in Python?
- In Python, a function is a group of related statements that performs a specific task.
- Functions help break our program into smaller and modular chunks. As our program grows larger and larger, functions make it more organized and manageable.
- Furthermore, it avoids repetition and makes the code reusable.
Syntax of Function
def function_name(parameters):
"""docstring"""
statement(s)
- Above shown is a function definition that consists of the following components.
- Keyword def that marks the start of the function header.
- A function name to uniquely identify the function. Function naming follows the same rules of writing identifiers in Python.
- Parameters (arguments) through which we pass values to a function. They are optional.
- A colon (:) to mark the end of the function header.
- Optional documentation string (docstring) to describe what the function does.
- One or more valid python statements that make up the function body. Statements must have the same indentation level (usually 4 spaces).
- An optional return statement to return a value from the function.
Example of a function
def greet(name):
"""
This function greets to
the person passed in as
a parameter
"""
print("Hello, " + name + ". Good morning!")
How to call a function in python?
- Once we have defined a function, we can call it from another function, program or even the Python prompt.
- To call a function we simply type the function name with appropriate parameters.
>>> greet('Paul')
Hello, Paul. Good morning!
Note: Try running the above code in the Python program with the function definition to see the output.
def greet(name):
"""
This function greets to
the person passed in as
a parameter
"""
print("Hello, " + name + ". Good morning!")
greet('Paul')
Python Docstrings
Python documentation strings (or docstrings) provide a convenient way of associating documentation with Python modules, functions, classes, and methods.
Declaring Docstrings: The docstrings are declared using ”’triple single quotes”’ or “””triple double quotes””” just below the class, method or function declaration. All functions should have a docstring.
Accessing Docstrings: The docstrings can be accessed using the __doc__ method of the object or using the help function.
The below examples demonstrates how to declare and access a docstring.
def my_function():
'''Demonstrates triple double quotes
docstrings and does nothing really.'''
return None
print("Using __doc__:")
print(my_function.__doc__)
print("Using help:")
help(my_function)
The return statement
The return statement is used to exit a function and go back to the place from where it was called.
Syntax of return
return [expression_list]
This statement can contain an expression that gets evaluated and the value is returned. If there is no expression in the statement or the return statement itself is not present inside a function, then the function will return the None object.
Scope and Lifetime of variables
Scope of a variable is the portion of a program where the variable is recognized. Parameters and variables defined inside a function are not visible from outside the function. Hence, they have a local scope.
The lifetime of a variable is the period throughout which the variable exits in the memory. The lifetime of variables inside a function is as long as the function executes.
They are destroyed once we return from the function. Hence, a function does not remember the value of a variable from its previous calls.
Here is an example to illustrate the scope of a variable inside a function.
def my_func():
x = 10
print("Value inside function:",x)
x = 20
my_func()
print("Value outside function:",x)
Output
Value inside function: 10
Value outside function: 20
Here, we can see that the value of x is 20 initially. Even though the function my_func() changed the value of x to 10, it did not affect the value outside the function.
This is because the variable x inside the function is different (local to the function) from the one outside. Although they have the same names, they are two different variables with different scopes.
On the other hand, variables outside of the function are visible from inside. They have a global scope.
We can read these values from inside the function but cannot change (write) them. In order to modify the value of variables outside the function, they must be declared as global variables using the keyword global.
Types of Functions
Basically, we can divide functions into the following two types:
- Built-in functions – Functions that are built into Python.
- User-defined functions – Functions defined by the users themselves.
Python Function Arguments
Variable Function Arguments
- Up until now, functions had a fixed number of arguments. In Python, there are other ways to define a function that can take variable number of arguments.
- Three different forms of this type are described below.
Python Default Arguments
Function arguments can have default values in Python.
We can provide a default value to an argument by using the assignment operator (=). Here is an example.
def greet(name, msg="Good morning!"):
"""
This function greets to
the person with the
provided message.
If the message is not provided,
it defaults to "Good
morning!"
"""
print("Hello", name + ', ' + msg)
greet("Kate")
greet("Bruce", "How do you do?")
Output
Hello Kate, Good morning!
Hello Bruce, How do you do?
- In this function, the parameter name does not have a default value and is required (mandatory) during a call.
- On the other hand, the parameter msg has a default value of “Good morning!”. So, it is optional during a call. If a value is provided, it will overwrite the default value.
- Any number of arguments in a function can have a default value.
- But once we have a default argument, all the arguments to its right must also have default values.
This means to say, non-default arguments cannot follow default arguments. For example, if we had defined the function header above as:
def greet(msg = "Good morning!", name):
We would get an error as:
SyntaxError: non-default argument follows default argument
Python Keyword Arguments
When we call a function with some values, these values get assigned to the arguments according to their position.
For example, in the above function greet(), when we called it as greet(“Bruce”, “How do you do?”), the value “Bruce” gets assigned to the argument name and similarly “How do you do?” to msg.
Python allows functions to be called using keyword arguments. When we call functions in this way, the order (position) of the arguments can be changed. Following calls to the above function are all valid and produce the same result.
# 2 keyword arguments
greet(name = "Bruce",msg = "How do you do?")
# 2 keyword arguments (out of order)
greet(msg = "How do you do?",name = "Bruce")
1 positional, 1 keyword argument
greet("Bruce", msg = "How do you do?")
As we can see, we can mix positional arguments with keyword arguments during a function call. But we must keep in mind that keyword arguments must follow positional arguments.
Having a positional argument after keyword arguments will result in errors. For example, the function call as follows:
greet(name="Bruce","How do you do?")
Will result in an error:
SyntaxError: non-keyword arg after keyword arg
Python Arbitrary Arguments
Sometimes, we do not know in advance the number of arguments that will be passed into a function. Python allows us to handle this kind of situation through function calls with an arbitrary number of arguments.
In the function definition, we use an asterisk (*) before the parameter name to denote this kind of argument. Here is an example.
def greet(*names):
"""This function greets all
the person in the names tuple."""
# names is a tuple with arguments
for name in names:
print("Hello", name)
greet("Monica", "Luke", "Steve", "John")
Output
Hello Monica
Hello Luke
Hello Steve
Hello John
*args and **kwargs in Python
In Python, we can pass a variable number of arguments to a function using special symbols. There are two special symbols:
Special Symbols Used for passing arguments:-
1.)*args (Non-Keyword Arguments)
2.)**kwargs (Keyword Arguments)
Understanding *args
The special syntax *args in function definitions in python is used to pass a variable number of arguments to a function. It is used to pass a non-key worded, variable-length argument list.
- For example : we want to make a multiply function that takes any number of arguments and able to multiply them all together. It can be done using *args.
- Using the *, the variable that we associate with the * becomes an iterable meaning you can do things like iterate over it, run some higher-order functions such as map and filter, etc.
In the code above, we built the function with x and y as arguments, and then when we call the function, we need to use numbers to correspond with x and y. In this case, we will pass the integer 5 in for x and the integer 4 in for y:
def multiply(x, y):
print (x * y)
multiply(5, 4)
What if, later on, we decide that we would like to multiply three numbers rather than just two? If we try to add an additional number to the function, as shown below, we’ll receive an error.
def multiply(x, y):
print (x * y)
multiply(5, 4, 3)
TypeError: multiply() takes 2 positional arguments but 3 were given
So, if you suspect that you may need to use more arguments later on, you can make use of *args as your parameter instead.
We can essentially create the same function and code that we showed in the first example, by removing x and y as function parameters, and instead replacing them with *args:
def multiply(*args):
z = 1
for num in args:
z *= num
print(z)
multiply(4, 5)
multiply(10, 9)
multiply(2, 3, 4)
multiply(3, 5, 10, 6)
With *args you can create more flexible code that accepts a varied amount of non-keyworded arguments within your function.
Understanding **kwargs
The double asterisk form of **kwargs is used to pass a keyworded, variable-length argument dictionary to a function. Again, the two asterisks (**) are the important element here, as the word kwargs is conventionally used, though not enforced by the language.
Like *args, **kwargs can take however many arguments you would like to supply to it. However, **kwargs differs from *args in that you will need to assign keywords.
First, let’s simply print out the **kwargs arguments that we pass to a function. We’ll create a short function to do this:
def print_kwargs(**kwargs):
print(kwargs)
Next, we’ll call the function with some keyworded arguments passed into the function:
def print_kwargs(**kwargs):
print(kwargs)
print_kwargs(kwargs_1="Shark", kwargs_2=4.5, kwargs_3=True)
Let’s run the program above and look at the output:
python print_kwargs.py
Output
{'kwargs_3': True, 'kwargs_2': 4.5, 'kwargs_1': 'Shark'}
Depending on the version of Python 3 you are currently using, the dictionary data type may be unordered. In Python 3.6 and above, you’ll receive the key-value pairs in order, but in earlier versions, the pairs will be output in a random order.
What is important to note is that a dictionary called kwargs is created and we can work with it just like we can work with other dictionaries.
Let’s create another short program to show how we can make use of **kwargs. Here we’ll create a function to greet a dictionary of names. First, we’ll start with a dictionary of two names:
def print_values(**kwargs):
for key, value in kwargs.items():
print("The value of {} is {}".format(key, value))
print_values(my_name="Sammy", your_name="Casey")
We can now run the program and look at the output:
python print_values.py
Output
The value of your_name is Casey
The value of my_name is Sammy
Again, because dictionaries may be unordered, your output may be with the name Casey first or with the name Sammy first.
Let’s now pass additional arguments to the function to show that **kwargs will accept however many arguments you would like to include:
def print_values(**kwargs):
for key, value in kwargs.items():
print("The value of {} is {}".format(key, value))
print_values(
name_1="Alex",
name_2="Gray",
name_3="Harper",
name_4="Phoenix",
name_5="Remy",
name_6="Val"
)
When we run the program at this point, we’ll receive the following output, which may again be unordered:
Output
The value of name_2 is Gray
The value of name_6 is Val
The value of name_4 is Phoenix
The value of name_5 is Remy
The value of name_3 is Harper
The value of name_1 is Alex
Ordering Arguments
When ordering arguments within a function or function call, arguments need to occur in a particular order:
- Formal positional arguments
- *args
- Keyword arguments
- **kwargs
In practice, when working with explicit positional parameters along with *args and **kwargs, your function would look like this:
def example(arg_1, arg_2, *args, **kwargs):
And, when working with positional parameters along with named keyword parameters in addition to *args and **kwargs, your function would look like this:
def example2(arg_1, arg_2, *args, kw_1=”shark”, kw_2=”blobfish”, **kwargs):
It is important to keep the order of arguments in mind when creating functions so that you do not receive a syntax error in your Python code.
Using *args and **kwargs in Function Calls
We can also use *args and **kwargs to pass arguments into functions.
First, let’s look at an example with *args.
some_args.py
def some_args(arg_1, arg_2, arg_3):
print(“arg_1:”, arg_1)
print(“arg_2:”, arg_2)
print(“arg_3:”, arg_3)
args = (“Sammy”, “Casey”, “Alex”)
some_args(*args)
In the function above, there are three parameters defined as arg_1, arg_, and arg_3. The function will print out each of these arguments. We then create a variable that is set to an iterable (in this case, a tuple), and can pass that variable into the function with the asterisk syntax.
When we run the program with the python some_args.py command, we’ll receive the following output:
Output
arg_1: Sammy
arg_2: Casey
arg_3: Alex
We can also modify the program above to an iterable list data type with a different variable name. Let’s also combine the *args syntax with a named parameter:
some_args.py
def some_args(arg_1, arg_2, arg_3):
print(“arg_1:”, arg_1)
print(“arg_2:”, arg_2)
print(“arg_3:”, arg_3)
my_list = [2, 3]
some_args(1, *my_list)
Copy
If we run the program above, it will produce the following output:
Output
arg_1: 1
arg_2: 2
arg_3: 3
Similarly, the keyworded **kwargs arguments can be used to call a function. We will set up a variable equal to a dictionary with 3 key-value pairs (we’ll use kwargs here, but it can be called whatever you want), and pass it to a function with 3 arguments:
some_kwargs.py
def some_kwargs(kwarg_1, kwarg_2, kwarg_3):
print(“kwarg_1:”, kwarg_1)
print(“kwarg_2:”, kwarg_2)
print(“kwarg_3:”, kwarg_3)
kwargs = {“kwarg_1”: “Val”, “kwarg_2”: “Harper”, “kwarg_3”: “Remy”}
some_kwargs(**kwargs)
Copy
Let’s run the program above with the python some_kwargs.py command:
Output
kwarg_1: Val
kwarg_2: Harper
kwarg_3: Remy
When calling a function, you can use *args and **kwargs to pass arguments.
Python Recursion
What is recursion?
Recursion is the process of defining something in terms of itself.
A physical world example would be to place two parallel mirrors facing each other. Any object in between them would be reflected recursively.
Python Recursive Function
In Python, we know that a function can call other functions. It is even possible for the function to call itself. These types of construct are termed as recursive functions.
The following image shows the working of a recursive function called recurse.
Recursive Function in Python
Following is an example of a recursive function to find the factorial of an integer.
Factorial of a number is the product of all the integers from 1 to that number. For example, the factorial of 6 (denoted as 6!) is 1*2*3*4*5*6 = 720.
Example of a recursive function
def factorial(x):
“””This is a recursive function
to find the factorial of an integer”””
if x == 1:
return 1
else:
return (x * factorial(x-1))
num = 3
print(“The factorial of”, num, “is”, factorial(num))
Output
The factorial of 3 is 6
In the above example, factorial() is a recursive function as it calls itself.
When we call this function with a positive integer, it will recursively call itself by decreasing the number.
Each function multiplies the number with the factorial of the number below it until it is equal to one. This recursive call can be explained in the following steps.
factorial(3) # 1st call with 3
3 * factorial(2) # 2nd call with 2
3 * 2 * factorial(1) # 3rd call with 1
3 * 2 * 1 # return from 3rd call as number=1
3 * 2 # return from 2nd call
6 # return from 1st call
Let’s look at an image that shows a step-by-step process of what is going on:
Python lambda (Anonymous Functions) | filter, map, reduce
- In Python, anonymous function means that a function is without a name.
- As we already know that def keyword is used to define the normal functions and the lambda keyword is used to create anonymous functions.
- It has the following syntax:
lambda arguments: expression
- This function can have any number of arguments but only one expression, which is evaluated and returned.
- One is free to use lambda functions wherever function objects are required.
- You need to keep in your knowledge that lambda functions are syntactically restricted to a single expression.
- It has various uses in particular fields of programming besides other types of expressions in functions.
# Python code to illustrate cube of a number
# showing difference between def() and lambda().
def cube(y):
return y*y*y;
g = lambda x: x*x*x
print(g(7))
print(cube(5))
- Without using Lambda : Here, both of them returns the cube of a given number. But, while using def, we needed to define a function with a name cube and needed to pass a value to it. After execution, we also needed to return the result from where the function was called using the return keyword.
- Using Lambda : Lambda definition does not include a “return” statement, it always contains an expression which is returned. We can also put a lambda definition anywhere a function is expected, and we don’t have to assign it to a variable at all. This is the simplicity of lambda functions.
Map
The built-in function map() takes a function as a first argument and applies it to each of the elements of its second argument, an iterable. Examples of iterables are strings, lists, and tuples. For more information on iterables and iterators, check out Iterables and Iterators.
map() returns an iterator corresponding to the transformed collection. As an example, if you wanted to transform a list of strings to a new list with each string capitalized, you could use map(), as follows:
>>> list(map(lambda x: x.capitalize(), [‘cat’, ‘dog’, ‘cow’]))
[‘Cat’, ‘Dog’, ‘Cow’]
You need to invoke list() to convert the iterator returned by map() into an expanded list that can be displayed in the Python shell interpreter.
Using a list comprehension eliminates the need for defining and invoking the lambda function:
>>> [x.capitalize() for x in [‘cat’, ‘dog’, ‘cow’]]
[‘Cat’, ‘Dog’, ‘Cow’]
Example use with map()
The map() function in Python takes in a function and a list.
The function is called with all the items in the list and a new list is returned which contains items returned by that function for each item.
Here is an example use of map() function to double all the items in a list.
# Program to double each item in a list using map()
my_list = [1, 5, 4, 6, 8, 11, 3, 12]
new_list = list(map(lambda x: x * 2 , my_list))
print(new_list)
Output
[2, 10, 8, 12, 16, 22, 6, 24]
Filter
The built-in function filter(), another classic functional construct, can be converted into a list comprehension. It takes a predicate as a first argument and an iterable as a second argument. It builds an iterator containing all the elements of the initial collection that satisfies the predicate function. Here’s an example that filters all the even numbers in a given list of integers:
>>> even = lambda x: x%2 == 0
>>> list(filter(even, range(11)))
[0, 2, 4, 6, 8, 10]
Note that filter() returns an iterator, hence the need to invoke the built-in type list that constructs a list given an iterator.
The implementation leveraging the list comprehension construct gives the following:
>>> [x for x in range(11) if x%2 == 0]
[0, 2, 4, 6, 8, 10]
Example use with filter()
The filter() function in Python takes in a function and a list as arguments.
The function is called with all the items in the list and a new list is returned which contains items for which the function evaluates to True.
Here is an example use of filter() function to filter out only even numbers from a list.
# Program to filter out only the even items from a list
my_list = [1, 5, 4, 6, 8, 11, 3, 12]
new_list = list(filter(lambda x: (x%2 == 0) , my_list))
print(new_list)
Output
[4, 6, 8, 12]
Python Global, Local and Nonlocal variables
Global Variables
In Python, a variable declared outside of the function or in global scope is known as a global variable. This means that a global variable can be accessed inside or outside of the function.
Let’s see an example of how a global variable is created in Python.
Example 1: Create a Global Variable
x = “global”
def foo():
print(“x inside:”, x)
foo()
print(“x outside:”, x)
Output
x inside: global
x outside: global
In the above code, we created x as a global variable and defined a foo() to print the global variable x. Finally, we call the foo() which will print the value of x.
What if you want to change the value of x inside a function?
x = “global”
def foo():
x = x * 2
print(x)
foo()
Output
UnboundLocalError: local variable ‘x’ referenced before assignment
The output shows an error because Python treats x as a local variable and x is also not defined inside foo().
To make this work, we use the global keyword.
Local Variables
A variable declared inside the function’s body or in the local scope is known as a local variable.
Example 2: Accessing local variable outside the scope
def foo():
y = “local”
foo()
print(y)
Output
NameError: name ‘y’ is not defined
The output shows an error because we are trying to access a local variable y in a global scope whereas the local variable only works inside foo() or local scope.
Let’s see an example on how a local variable is created in Python.
Example 3: Create a Local Variable
Normally, we declare a variable inside the function to create a local variable.
def foo():
y = “local”
print(y)
foo()
Output
local
Nonlocal Variables
Nonlocal variables are used in nested functions whose local scope is not defined. This means that the variable can be neither in the local nor the global scope.
Let’s see an example of how a nonlocal variable is used in Python.
We use nonlocal keywords to create nonlocal variables.
Example 6: Create a nonlocal variable
def outer():
x = “local”
def inner():
nonlocal x
x = “nonlocal”
print(“inner:”, x)
inner()
print(“outer:”, x)
outer()
Output
inner: nonlocal
outer: nonlocal
In the above code, there is a nested inner() function. We use nonlocal keywords to create a nonlocal variable. The inner() function is defined in the scope of another function outer().
Python Global Keyword
What is the global keyword
In Python, global keyword allows you to modify the variable outside of the current scope. It is used to create a global variable and make changes to the variable in a local context.
Rules of global Keyword
The basic rules for global keyword in Python are:
- When we create a variable inside a function, it is local by default.
- When we define a variable outside of a function, it is global by default. You don’t have to use global keyword.
- We use global keyword to read and write a global variable inside a function.
- Use of global keyword outside a function has no effect.
Use of global Keyword
Let’s take an example.
Example 1: Accessing global Variable From Inside a Function
c = 1 # global variable
def add():
print(c)
add()
When we run the above program, the output will be:
1
Example 2: Modifying Global Variable From Inside the Function
c = 1 # global variable
def add():
c = c + 2 # increment c by 2
print(c)
add()
When we run the above program, the output shows an error:
UnboundLocalError: local variable ‘c’ referenced before assignment
This is because we can only access the global variable but cannot modify it from inside the function.
The solution for this is to use the global keyword.
Example 3: Changing Global Variable From Inside a Function using global
c = 0 # global variable
def add():
global c
c = c + 2 # increment by 2
print(“Inside add():”, c)
add()
print(“In main:”, c)
When we run the above program, the output will be:
Inside add(): 2
In main: 2
In the above program, we define c as a global keyword inside the add() function.
Then, we increment the variable c by 1, i.e c = c + 2. After that, we call the add() function. Finally, we print the global variable c.
As we can see, change also occurred on the global variable outside the function, c = 2.
Global Variables Across Python Modules
In Python, we create a single module config.py to hold global variables and share information across Python modules within the same program.
Here is how we can share global variables across the python modules.
Example 4: Share a global Variable Across Python Modules
Create a config.py file, to store global variables
a = 0
b = “empty”
Create a update.py file, to change global variables
import config
config.a = 10
config.b = “alphabet”
Create a main.py file, to test changes in value
import config
import update
print(config.a)
print(config.b)
When we run the main.py file, the output will be
10
alphabet
In the above, we have created three files: config.py, update.py, and main.py.
The module config.py stores global variables of a and b. In the update.py file, we import the config.py module and modify the values of a and b. Similarly, in the main.py file, we import both config.py and update.py module. Finally, we print and test the values of global variables whether they are changed or not.
Python Modules
What are modules in Python?
Modules refer to a file containing Python statements and definitions.
A file containing Python code, for example: example.py, is called a module, and its module name would be example.
We use modules to break down large programs into small manageable and organized files. Furthermore, modules provide reusability of code.
We can define our most used functions in a module and import it, instead of copying their definitions into different programs.
Let us create a module. Type the following and save it as example.py.
# Python Module example
def add(a, b):
“””This program adds two
numbers and return the result”””
result = a + b
return result
Here, we have defined a function add() inside a module named example. The function takes in two numbers and returns their sum.
How to import modules in Python?
We can import the definitions inside a module to another module or the interactive interpreter in Python.
We use the import keyword to do this. To import our previously defined module example, we type the following in the Python prompt.
>>> import example
There are various ways to import modules. They are listed below..
Python import statement
We can import a module using the import statement and access the definitions inside it using the dot operator as described above. Here is an example.
# import statement example
# to import standard module math
import math
print(“The value of pi is”, math.pi)
When you run the program, the output will be:
The value of pi is 3.141592653589793
Import with renaming
We can import a module by renaming it as follows:
# import module by renaming it
import math as m
print(“The value of pi is”, m.pi)
Python from…import statement
We can import specific names from a module without importing the module as a whole. Here is an example.
# import only pi from math module
from math import pi
print(“The value of pi is”, pi)
Here, we imported only the pi attribute from the math module.
Import all names
We can import all names(definitions) from a module using the following construct:
# import all names from the standard module math
from math import *
print(“The value of pi is”, pi)
Python Module Search Path
While importing a module, Python looks at several places. Interpreter first looks for a built-in module. Then(if built-in module not found), Python looks into a list of directories defined in sys.path. The search is in this order.
- The current directory.
- PYTHONPATH (an environment variable with a list of directories).
- The installation-dependent default directory.
>>> import sys
>>> sys.path
[”,
‘C:\\Python33\\Lib\\idlelib’,
‘C:\\Windows\\system32\\python33.zip’,
‘C:\\Python33\\DLLs’,
‘C:\\Python33\\lib’,
‘C:\\Python33’,
‘C:\\Python33\\lib\\site-packages’]
We can add and modify this list to add our own path.
Reloading a module
The Python interpreter imports a module only once during a session. This makes things more efficient. Here is an example to show how this works.
Suppose we have the following code in a module named my_module
The dir() built-in function
We can use the dir() function to find out names that are defined inside a module.
For example, we have defined a function add() in the module example that we had in the beginning.
We can use dir in example module in the following way:
>>> dir(example)
[‘__builtins__’,
‘__cached__’,
‘__doc__’,
‘__file__’,
‘__initializing__’,
‘__loader__’,
‘__name__’,
‘__package__’,
‘add’]
Here, we can see a sorted list of names (along with add). All other names that begin with an underscore are default Python attributes associated with the module (not user-defined).
For example, the __name__ attribute contains the name of the module.
>>> import example
>>> example.__name__
‘example’
Python Package
a directory can contain subdirectories and files, a Python package can have sub-packages and modules.
A directory must contain a file named __init__.py in order for Python to consider it as a package. This file can be left empty but we generally place the initialization code for that package in this file.
Here is an example. Suppose we are developing a game. One possible organization of packages and modules could be as shown in the figure below.
Package Module Structure in Python Programming
Importing module from a package
We can import modules from packages using the dot (.) operator.
For example, if we want to import the start module in the above example, it can be done as follows:
import Game.Level.start
Now, if this module contains a function named select_difficulty(), we must use the full name to reference it.
Game.Level.start.select_difficulty(2)
If this construct seems lengthy, we can import the module without the package prefix as follows:
from Game.Level import start
We can now call the function simply as follows:
start.select_difficulty(2)