Functions
Functions
ipynb - Colab
keyboard_arrow_down Functions
In programming, a function is a block of organized, reusable code that performs a specific task
or a set of instructions. Functions provide a way to break down complex problems into smaller,
manageable parts, making the code more modular, readable, and maintainable. They play a
crucial role in programming by promoting code reuse and improving overall program structure.
Motivation
Welcome to your journey of learning about functions in programming! Let's explore the key
reasons why learning about functions is valuable:
Modular and Reusable Code: Functions are like special boxes that hold sets of
instructions. When you put instructions inside a function, you can use those instructions
again and again in your program. It's like having a handy tool that you can use whenever
you need it. Functions help you organize your code, avoid repeating the same instructions,
and make your code easier to maintain and grow.
Abstraction and Code Clarity: Functions help simplify complicated things in your code.
They let you give a name to a set of instructions, so you don't have to remember all the
details. It's like using easy words instead of difficult ones. Functions also make your code
easier to understand because you can read the names and know what each part does.
When your code is clear, it's easier for others (including yourself in the future) to
understand and use it.
Code Separation and Modifiability: Functions help you separate and organize your code
into smaller parts. It's like having different sections for different tasks. This makes your
code easier to understand, find problems, and make changes. It's also like having a team of
people working together on different parts of the code. With functions, you can collaborate
with others and make your code better and easier to maintain.
def function_name(parameters):
# Function body
# Code to be executed
return value
Parentheses: The parentheses () following the function name are used to enclose the
function's parameters. Parameters are optional and can be used to pass values into the
function.
Colons: The colon : at the end of the function definition is used to indicate the start of the
function body. It is a required part of the syntax and distinguishes the function definition
from the code inside the function.
Now that we understand how to define a function, let's write our first function. Here's an
example:
When naming functions, it is essential to follow certain naming conventions to enhance code
readability and maintain consistency. Here are some common conventions:
Use descriptive names: Choose names that clearly indicate the purpose or action
performed by the function
Use lowercase letters and underscores: For readability, it is common to use lowercase
letters with underscores to separate words in function names (e.g., calculate_area ,
print_report )
Be consistent: Follow a consistent naming convention throughout your codebase to
maintain clarity and make your code more approachable for other developers
Let's look at some examples of good and bad function names following the naming conventions.
Good Examples:
calculate_area : This function name clearly indicates its purpose: to calculate the area of
a shape. It follows the convention of using lowercase letters and underscores to separate
words.
print_report : This function name describes its action: printing a report. It is easy to
understand and follows the convention of using lowercase letters and underscores.
validate_email : This function name indicates that it is used for email validation. It follows
the convention of using lowercase letters and underscores.
Bad Examples:
fn1 : This function name is not descriptive and does not provide any information about its
purpose or action. It does not follow the convention of using descriptive names.
Function_A : This function name uses capital letters and underscores, which is not
consistent with the convention of using lowercase letters. Additionally, the name does not
provide any specific information about the function's purpose.
a - This function name is too short and lacks clarity. It does not follow the convention of
using descriptive names.
Remember, using descriptive and meaningful names for functions enhances code readability
and makes it easier for others (including your future self) to understand and maintain the code.
To call a function, you simply write the function name followed by parentheses
() .
# Function definition
def greet():
print("Hello, there!")
# Function call
greet()
Hello, there!
In this example, the greet function is called by writing its name followed by parentheses. The
code inside the function's body, in this case, printing "Hello, there!" , is executed when the
function is called.
Parameters are variables defined in the function declaration, while arguments are
the actual values passed to a function during its call.
Parameters in Functions
Parameters are variables defined in the function's declaration that serve as placeholders for the
values that will be passed to the function when it is called. They define the input requirements
for the function.
In this example, the greet function has one parameter named name . It specifies that the
function expects an argument to be provided when it is called, which will be used to greet the
person by name.
Required Arguments: These are arguments that must be provided in the same order as the
function's parameter list. Failure to provide a required argument will result in an error.
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/Users/maya/Desktop/AiCore Work/Content-Projects/Content/units/Essentials/7. Python
programming/6. Functions and function calls/Notebook.ipynb Cell 15 in <cell line: 4>
()
<a href='vscode-notebook-cell:/Users/maya/Desktop/AiCore%20Work/Content-
Projects/Content/units/Essentials/7.%20Python%20programming/6.%20Functions%20and%20fu
line=0'>1</a> def divide_numbers(a, b):
<a href='vscode-notebook-cell:/Users/maya/Desktop/AiCore%20Work/Content-
Projects/Content/units/Essentials/7.%20Python%20programming/6.%20Functions%20and%20fu
line=1'>2</a> return a / b
----> <a href='vscode-notebook-cell:/Users/maya/Desktop/AiCore%20Work/Content-
In this example, the divide_numbers function expects two arguments: a and b . The function
divides a by b and returns the result . However, when calling the function, we only provide one
argument ( 10 ) instead of the required two. As a result, the function call raises a TypeError
because the required argument b is missing.
To fix this error, we need to provide both required arguments when calling the function. For
example:
result = divide_numbers(10, 2)
print("The result is:", result)
In this corrected example, we provide both 10 and 2 as arguments when calling the
divide_numbers function. The function executes successfully and returns the correct result,
which is then printed.
Optional Arguments: These arguments are not mandatory and have default values
assigned to them in the function's parameter list. They provide flexibility in function calls.
Hello, Alice!
Hi, Bob!
So remember, when calling a function, you pass arguments inside the parentheses
based on the function's parameter list. The order of the arguments should match
the order of the parameters.
To return a value from a function, you use the return keyword followed by the value (or
expression) you want to send back. Once a return statement is encountered, the function
immediately exits, and the returned value is passed to the caller.
result = add_numbers(3, 4)
print("The sum is:", result)
In this example, the add_numbers function returns the sum of two numbers. The return
statement return a + b sends the sum back to the caller, which assigns the returned value to the
result variable.
def get_name_and_age():
name = "Alice"
age = 25
return name, age
Name: Alice
Age: 25
In this example, the get_name_and_age function returns both the name and age . By using the
syntax name, age = get_name_and_age() , the returned tuple is unpacked, and the values are
assigned to the respective variables.
Return statements allow functions to produce results and share data with the
calling code. By understanding how to use return statements, return values from
functions, and handle multiple return values, you can effectively utilize functions to
perform computations and retrieve meaningful results.
Local Scope
Local scope refers to the region or context within a program where a variable is defined and
accessible. Variables that are defined within a specific block of code, such as a function, have
local scope and are limited to that block. They cannot be accessed from outside that block or in
other parts of the program.
def my_function():
local_var = 10 # Local variable
print(local_var)
my_function()
print(local_var) # Error: local_var is not defined outside the function
10
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
/Users/maya/Desktop/AiCore Work/Content-Projects/Content/units/Essentials/7. Python
programming/6. Functions and function calls/Notebook.ipynb Cell 27 in <cell line: 6>
()
<a href='vscode-notebook-cell:/Users/maya/Desktop/AiCore%20Work/Content-
Projects/Content/units/Essentials/7.%20Python%20programming/6.%20Functions%20and%20fu
line=2'>3</a> print(local_var)
<a href='vscode-notebook-cell:/Users/maya/Desktop/AiCore%20Work/Content-
Projects/Content/units/Essentials/7.%20Python%20programming/6.%20Functions%20and%20fu
line=4'>5</a> my_function()
----> <a href='vscode-notebook-cell:/Users/maya/Desktop/AiCore%20Work/Content-
In this example, the local_var is a local variable defined within the my_function function. It is
accessible only within the function's scope. Trying to access local_var outside the function
will result in an error because it is not defined in the global scope.
Local scope provides isolation for variables, meaning that variables with the same
name can be used independently in different functions without conflict. Each
function creates its own local scope, allowing you to reuse variable names without
causing conflicts or interference between functions.
def func1():
x = 10
print(x)
def func2():
x = 20
print(x)
func1() # Outputs: 10
func2() # Outputs: 20
10
20
In this example, both func1 and func2 define a variable named x within their local scope. Each
function can use and manipulate its own x variable without affecting the other. This
demonstrates the isolation and reusability of variables within local scope.
def outer_function():
outer_variable = "I am outer"
def inner_function():
inner_variable = "I am inner"
print(inner_variable)
print(outer_variable) # Can access outer_variable defined in the outer function
inner_function()
outer_function()
I am inner
I am outer
In this example, the outer_function defines the outer_variable . Inside the outer_function ,
there is another function called inner_function . The inner_function has access to both its
own local variable inner_variable and the outer_variable defined in the outer function.
However, neither inner_variable nor outer_variable is accessible outside their respective
functions.
Global scope refers to the region outside of any function or block where variables are defined.
Variables declared outside of functions, at the top level of a program, have global scope and can
be accessed throughout the program.
def print_global():
print("Inside the function:", global_var)
print_global()
print("Outside the function:", global_var)
In this example, the variable global_var is defined outside of any function, making it a global
variable. It can be accessed both inside and outside functions, allowing you to use its value in
various parts of the program.
Remember, to access a variable, you need to consider its scope. Variables defined
in a local scope are accessible only within the same function, while variables
defined in the global scope can be accessed from any part of the program.
In Python, *args and **kwargs are special syntaxes used to pass a variable number of
arguments to a function. They allow functions to handle an arbitrary number of positional
arguments ( *args ) and keyword arguments ( **kwargs ).
The args syntax allows a function to accept a variable number of positional arguments. The
name args is a convention, but you can use any valid variable name preceded by an asterisk.
def my_function(*args):
print(*args)
1 2 3
The kwargs syntax allows a function to accept a variable number of keyword arguments. Similar
to *args , the name kwargs is a convention, and you can use any valid variable name preceded
by two asterisks.
def my_function(**kwargs):
for key, value in kwargs.items():
print(key, value)
name Alice
age 25
The arguments are collected into a dictionary named kwargs . Within the function,
you can access the key-value pairs in the kwargs dictionary and process them as
needed.
You can use *args and **kwargs together in a function definition to accept both positional and
keyword arguments.
1 2
('name', 'Alice') ('age', 25)
In this example, the my_function can accept both positional arguments ( 1 , 2 ) as part of *arg s
and keyword arguments ( name="Alice" , age=25 ) as part of **kwargs . You can access and
process both types of arguments separately within the function.
To pass multiple arguments to a function using *args and **kwargs , you can use the asterisk or
double asterisks when calling the function.
my_args = (1, 2)
my_kwargs = {"name": "Alice", "age": 25}
my_function(*my_args, **my_kwargs) # Unpacking the arguments using * and **
1 2
('name', 'Alice') ('age', 25)
In this example, the variables my_args and my_kwargs hold the arguments to be passed. The *
operator is used to unpack the values in my_args as positional arguments, while the ** operator
is used to unpack the key-value pairs in my_kwargs as keyword arguments.
len() : Returns the length of a variable, e.g. number of elements in a list, or number of
characters in a string
type() : Returns the type of an object, e.g. for a list, it will return list
map() : Applies a function to every item in an iterable e.g. map(my_function, my_list) will
call my_function on every item in my_list , and will return a map object containing all the
outputs, which can then be converted to a list:
my_list = [ 1, 2, 3]
squared_list = map(square_number, my_list)
print(f"'squared list' is variable of type {type(squared_list)}")
print(squared_list)
squared_list=list(squared_list)
print(f"'squared list' is now a variable of type {type(squared_list)}")
print(squared_list)
The above example defines a function called square_number which returns the square of the
input, and uses the built-in map() function to apply it to each element in my_list . By default,
map returns a map object, so to print the answer we must first convert the variable squared_list
to a list .
Base Case: A condition that specifies when the recursion should stop. It represents the
simplest form of the problem that can be solved directly.
Recursive Case: The part of the function that invokes itself, solving a smaller version of the
problem and making progress towards the base case
Factorial Calculation
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n - 1)
result = factorial(5)
print(result) # Outputs: 120
120
The factorial function calculates the factorial of a number n using recursion. The factorial of a
non-negative integer n is the product of all positive integers less than or equal to n .
The function takes an argument n , which represents the number for which we want to
calculate the factorial
The function begins with a base case:
If n is 0 , we have reached the base case. In this case, we simply return 1 since the
factorial of 0 is defined as 1
If the base case is not met (i.e., n is not 0 ), the function proceeds to the recursive case
In the recursive case:
The function calls itself with the argument n - 1 . This calculates the factorial of n -
1.
The return statement multiplies the result of the recursive call by n , the current value
The recursive calls continue until the base case is reached (when n becomes 0 ). At that
point, the function starts unwinding the recursion by returning the factorial values in
reverse order.
In the example provided ( result = factorial(5) ), we are calculating the factorial of 5 . The
recursive calls unfold as follows:
At this point, the base case is reached. Since factorial(0 ) returns 1 , the recursion starts
unwinding:
Finally, the calculated value, 120 , is assigned to the result variable and printed ( print(result) ).
def fibonacci(n):
if n <= 1:
return n
else:
return fibonacci(n - 1) + fibonacci(n - 2)
result = fibonacci(6)
print(result) # Outputs: 8
The fibonacci function calculates the n th Fibonacci number using recursion. The Fibonacci
sequence is a series of numbers in which each number (after the first two) is the sum of the two
preceding ones. In this case, we are starting the sequence with 0 and 1 .
The function takes an argument n , which represents the position of the desired Fibonacci
number in the sequence.
The function begins with a base case:
If n is less than or equal to 1 , we have reached the base case. In this case, we
simply return n itself. This covers the first two Fibonacci numbers, 0 and 1 , since
they are equal to their positions in the sequence.
If the base case is not met (i.e., n is greater than 1 ), the function proceeds to the
recursive case
In the recursive case:
The return statement combines the results of the recursive calls by adding them together
The recursive calls continue until the base case is reached (when n becomes 0 or 1 ). At
that point, the function starts unwinding the recursion by returning the Fibonacci numbers
in reverse order.
In the example provided ( result = fibonacci(6)) , we are calculating the 6th Fibonacci number.
The recursive calls unfold as follows:
Finally, the calculated value, 8 , is assigned to the result variable and printed ( print(result) ).
Recursive functions require careful consideration to avoid infinite recursion. Here are some
precautions to take:
Ensure a base case: Every recursive function must have a base case that will eventually be
reached, breaking the recursion
Ensure progress towards the base case: Recursive calls should move the problem closer
to the base case with each iteration. Otherwise, the function may enter an infinite loop.
Test with small inputs: Before applying recursive functions to large inputs, test them with
small inputs to verify correctness and efficiency
It's important to understand the nature of the problem and design the recursive function
accordingly to ensure it terminates and provides the desired results.
Writing functions that follow best practices is essential for producing clean, maintainable, and
reusable code. This section outlines some key best practices to keep in mind when working with
functions.
Syntax errors occur when the syntax of a function declaration or its statements is incorrect.
def my_function:
print("Hello, world!")
Input In [24]
def my_function:
^
SyntaxError: invalid syntax
Troubleshooting Tips
Check for missing parentheses, colons, commas, or other required syntax elements:
def my_function():
print("Hello, world!")
Verify that the function name is spelled correctly and follows the naming conventions
keyboard_arrow_down NameError
NameError s occur when you use a variable or function name that is not defined or out of scope.
def my_function():
print(my_variable)
Troubleshooting Tips
def my_function():
my_variable = "Hello, world!"
print(my_variable)
keyboard_arrow_down TypeError
TypeError s occur when you perform an operation on incompatible data types or pass incorrect
arguments to a function or when a function is called with an incorrect number of arguments.
Let's first look at an example of operations with incompatible data types:
https://colab.research.google.com/github/AI-Core/Content-Public/blob/main/Content/units/Essentials/7. Python programming/6. Functions /Not… 16/20
4/25/24, 10:36 PM Notebook.ipynb - Colab
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/Users/maya/Desktop/AiCore Work/Content-Projects/Content/units/Essentials/7. Python
programming/6. Functions and function calls/Notebook.ipynb Cell 59 in <cell line: 4>
()
<a href='vscode-notebook-cell:/Users/maya/Desktop/AiCore%20Work/Content-
Projects/Content/units/Essentials/7.%20Python%20programming/6.%20Functions%20and%20fu
line=0'>1</a> def divide_numbers(a, b):
<a href='vscode-notebook-cell:/Users/maya/Desktop/AiCore%20Work/Content-
Projects/Content/units/Essentials/7.%20Python%20programming/6.%20Functions%20and%20fu
line=1'>2</a> return a / b
----> <a href='vscode-notebook-cell:/Users/maya/Desktop/AiCore%20Work/Content-
Projects/Content/units/Essentials/7.%20Python%20programming/6.%20Functions%20and%20fu
line=3'>4</a> result = divide_numbers(10, "5")
Troubleshooting Tips
Verify that the arguments passed to the function are of the correct data types and in the
expected order:
result = divide_numbers(10, 5)
And now let's look at an example of calling a function with an incorrect number of arguments:
result = divide_numbers(10)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/Users/maya/Desktop/AiCore Work/Content-Projects/Content/units/Essentials/7. Python
programming/6. Functions /Notebook.ipynb Cell 63 in <cell line: 4>()
<a href='vscode-notebook-cell:/Users/maya/Desktop/AiCore%20Work/Content-
Projects/Content/units/Essentials/7.%20Python%20programming/6.%20Functions%20/Noteboo
line=0'>1</a> def divide_numbers(a, b):
<a href='vscode-notebook-cell:/Users/maya/Desktop/AiCore%20Work/Content-
Projects/Content/units/Essentials/7.%20Python%20programming/6.%20Functions%20/Noteboo
line=1'>2</a> return a / b
----> <a href='vscode-notebook-cell:/Users/maya/Desktop/AiCore%20Work/Content-
Projects/Content/units/Essentials/7.%20Python%20programming/6.%20Functions%20/Noteboo
Troubleshooting Tips
Verify that you pass the same number of arguments to the function as the number of
parameters in the function definition:
result = divide_numbers(10, 2)
keyboard_arrow_down ValueError
ValueError s occur when a function receives an argument of the correct type but with an invalid
value.
def convert_to_int(value):
return int(value)
result = convert_to_int("abc")
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
/Users/maya/Desktop/AiCore Work/Content-Projects/Content/units/Essentials/7. Python
programming/6. Functions and function calls/Notebook.ipynb Cell 63 in <cell line: 4>
()
<a href='vscode-notebook-cell:/Users/maya/Desktop/AiCore%20Work/Content-
Projects/Content/units/Essentials/7.%20Python%20programming/6.%20Functions%20and%20fu
line=0'>1</a> def convert_to_int(value):
<a href='vscode-notebook-cell:/Users/maya/Desktop/AiCore%20Work/Content-
Projects/Content/units/Essentials/7.%20Python%20programming/6.%20Functions%20and%20fu
line=1'>2</a> return int(value)
----> <a href='vscode-notebook-cell:/Users/maya/Desktop/AiCore%20Work/Content-
Projects/Content/units/Essentials/7.%20Python%20programming/6.%20Functions%20and%20fu
line=3'>4</a> result = convert_to_int("abc")
Troubleshooting Tips:
Check if the input values meet the requirements specified by the function:
result = convert_to_int("123")
def countdown(n):
print(n)
countdown(n - 1)
countdown(5)
Troubleshooting Tips:
Review your recursive function to ensure it has a base case that terminates the recursion:
def countdown(n):
if n <= 0:
return
print(n)
countdown(n - 1)
countdown(5)
Check if the recursive function is invoked with the correct arguments to make progress
towards the base case.
By understanding these common function errors and following the troubleshooting tips, you can
effectively identify and resolve issues in your code, leading to more robust and error-free
functions.
Key Takeaways
Functions are important for organizing code, promoting reusability, and improving
readability
When defining functions, use the def keyword followed by the function name, parentheses,
and a colon
Function calls involve using the function name followed by parentheses, passing
arguments, and optionally assigning return values
Parameters are variables defined in the function declaration, while arguments are values
passed during function calls
Return statements specify the value that a function should return, and functions can return
multiple values