Lecture 7 โ Functions, Modules, and Packages#
๐ฏ Learning Objectives
By the end of this lecture, you will be able to:
- โ Define functions with parameters, defaults, and return values
- โ Understand variable scope: local vs. global
- โ
Use
*argsand**kwargsfor flexible arguments - โ Import and use modules and packages
- โ Create your own reusable modules
๐ Table of Contents
- โ โ๏ธ Lecture 7 โ Functions, Modules, and Packages
- 1. โธ ๐ 0. Functions
- 2. โธ ๐ Recursive Function
- 3. โ Using named parameters:
- 4. โ Providing a dictionary:
- 5. โ Using named parameters:
- 6. โ Providing a dictionary:
- 7. โธ ๐ฆ 1. Modules
- 8. โธ ๐ 2. Package
- 9. โธ ๐ฏ Key Takeaways
๐ Building on What You Know
You've been writing code that runs from top to bottom. As your programs grow, you'll find yourself copying the same block of code in multiple places. Functions let you write it once and reuse it everywhere. Modules and packages let you organize and share those functions across projects.
๐งพ Summary
This lecture aims to introduce the concepts of functions, modules, and packages in Python. We will explore the role and creation of functions for organizing and reusing code, delve into modules for structuring code into manageable files, and examine packages for grouping related modules. By understanding these components, you'll gain the skills to build modular and maintainable Python programs.
๐ Key Concepts
- ๐ Functions: Organizing and reusing code with defined operations
- ๐ฆ Modules: Structuring code into manageable files
- ๐ Packages: Grouping related modules for larger projects
- ๐๏ธ Modular Design: Building maintainable and scalable Python programs
๐ Resources
- ๐ GitHub Repository: PyPro-SCiDaS โ The Shell
- ๐ก Feel free to explore, fork, and practice before the session!
The concepts of functions, modules, and packages are foundational to writing effective and maintainable Python code.
- Functions are vital for encapsulating and reusing blocks of code. They allow you to perform specific tasks and operations without repeating code, enhancing clarity and reducing errors.
- Modules help in organizing related functions, classes, and variables into separate files. This modular approach simplifies code management and improves readability by keeping related components together and making code easier to debug and update.
- Packages take modularity a step further by allowing you to group related modules into directories. This hierarchical structure supports the management of larger codebases, helps avoid name conflicts, and facilitates better dependency management.
These elements are crucial for writing organized, scalable, and efficient code, making development more manageable and collaboration more effective.
๐ 0. Functions
Simply defined, a function is a piece of code, a set of instructions organized to perform one or more well-defined tasks. In Python, functions are categorized into two types: built-in functions and user-defined functions.
- Built-in functions are functions that are directly integrated into Python's standard library.
- User-defined functions are written either by the current user or by other users.
๐ง 0.0. Some built-in functions in Python
The print() function: As we already know, the print() function displays the values of specified objects on the screen:
x = 12
print(x)
y = [1, "Monday", "12", 5, 3, "test value"]
print(y)
You can replace the default separator (a space) with another character (or even no character) using the sep argument:
print("Hello", "everyone", sep="")
The input() function: The input() function allows the user to enter a value for a given argument:
print("Hello,", prenom)
ch = input()
num = int(ch) # Convert the string to an integer
print("The square of", num, "is", num**2)
Note: It is important to note that the input() function always returns a string. If you need the user to enter a numeric value, you will have to convert the entered value (which will be of type string) into a numeric type using built-in functions like int() (for integers) or float() (for floating-point numbers).
๐ค 0.1. User-defined functions
To define a function in Python, use the def keyword to declare the function name. The general syntax for defining a function is as follows:
"""Documentation for the function."""
<block_of_instructions>
In the function definition, the first string of characters (called a docstring) serves as documentation for the function, accessible via the interpreter using, for example, help(functionName), or functionName? in Jupyter. It should be relevant, concise, and comprehensive. It may also include usage examples.
๐ 0.1.0. Definition of a simple function without arguments
The example below illustrates the definition of a simple function without arguments. The purpose of this function is to print the first 20 values of the multiplication table for 8.
"""Prints the first 20 multiples of 8."""
for i in range(1, 21):
print(f"8 x {i} = {8 * i}")
# Calling the function
print_multiples_of_eight()
def multiplication_table_8():
"""
The purpose of this function is to display the first 20
values of the multiplication table for 8.
Input: None
Output: The multiplication table for 8
"""
n = 1
while n <= 20:
v = n * 8
print(n, 'x', 8, '=', v, sep=' ')
n = n + 1
To execute the function multiplication_table_8() that we just defined, simply reference it by its name as follows (anywhere in the main program):
multiplication_table_8() # Calls the function tableMultiplication8()
1 x 8 = 8
2 x 8 = 16
3 x 8 = 24
4 x 8 = 32
5 x 8 = 40
6 x 8 = 48
7 x 8 = 56
8 x 8 = 64
9 x 8 = 72
10 x 8 = 80
11 x 8 = 88
12 x 8 = 96
13 x 8 = 104
14 x 8 = 112
15 x 8 = 120
16 x 8 = 128
17 x 8 = 136
18 x 8 = 144
19 x 8 = 152
20 x 8 = 160
def print_multiples_of_eight():
"""Prints the first 20 multiples of 8."""
for i in range(1, 21):
print(f"8 x {i} = {8 * i}")
print_multiples_of_eight()
8 x 1 = 8
8 x 2 = 16
8 x 3 = 24
8 x 4 = 32
8 x 5 = 40
8 x 6 = 48
8 x 7 = 56
8 x 8 = 64
8 x 9 = 72
8 x 10 = 80
8 x 11 = 88
8 x 12 = 96
8 x 13 = 104
8 x 14 = 112
8 x 15 = 120
8 x 16 = 128
8 x 17 = 136
8 x 18 = 144
8 x 19 = 152
8 x 20 = 160
๐ Exercise: Propose a version of the function tableMultiplication8() using a for loop.
๐ฏ 0.1.1. Definition of a function with parameters as arguments
A parameter is a variable that takes a constant value. In the previous example, we created a multiplication table for 8. We can generalize this function so that it returns the multiplication table for any specified number as an argument. Since these numbers are parameters, we need to define a function where the arguments are parameters. See the example below:
def tableMultiplication(base):
n = 1
while n <=20 :
v=n*base
print(n, 'x', base, '=', v, sep =' ')
n = n +1
tableMultiplication(2) # returns the multiplication table for 2
print("============================\n")
tableMultiplication(8) # returns the multiplication table for 8
print("============================\n")
tableMultiplication(11) # returns the multiplication table for 11
1 x 2 = 2
2 x 2 = 4
3 x 2 = 6
4 x 2 = 8
5 x 2 = 10
6 x 2 = 12
7 x 2 = 14
8 x 2 = 16
9 x 2 = 18
10 x 2 = 20
11 x 2 = 22
12 x 2 = 24
13 x 2 = 26
14 x 2 = 28
15 x 2 = 30
16 x 2 = 32
17 x 2 = 34
18 x 2 = 36
19 x 2 = 38
20 x 2 = 40
============================
1 x 8 = 8
2 x 8 = 16
3 x 8 = 24
4 x 8 = 32
5 x 8 = 40
6 x 8 = 48
7 x 8 = 56
8 x 8 = 64
9 x 8 = 72
10 x 8 = 80
11 x 8 = 88
12 x 8 = 96
13 x 8 = 104
14 x 8 = 112
15 x 8 = 120
16 x 8 = 128
17 x 8 = 136
18 x 8 = 144
19 x 8 = 152
20 x 8 = 160
============================
1 x 11 = 11
2 x 11 = 22
3 x 11 = 33
4 x 11 = 44
5 x 11 = 55
6 x 11 = 66
7 x 11 = 77
8 x 11 = 88
9 x 11 = 99
10 x 11 = 110
11 x 11 = 121
12 x 11 = 132
13 x 11 = 143
14 x 11 = 154
15 x 11 = 165
16 x 11 = 176
17 x 11 = 187
18 x 11 = 198
19 x 11 = 209
20 x 11 = 220
# An example with a nicer display:
def multiplication_table(n):
"""
The purpose of this function is to display the multiplication table for a given number `n`.
Input: n (int) - The number for which the multiplication table will be generated.
Output: The multiplication table for `n`.
"""
print(f"Multiplication Table for {n}:")
print("=" * 25)
for i in range(1, 21):
result = i * n
print(f"{i:2} x {n:2} = {result:3}")
print("=" * 25)
# Example usage:
multiplication_table(8)
Multiplication Table for 8:
=========================
1 x 8 = 8
2 x 8 = 16
3 x 8 = 24
4 x 8 = 32
5 x 8 = 40
6 x 8 = 48
7 x 8 = 56
8 x 8 = 64
9 x 8 = 72
10 x 8 = 80
11 x 8 = 88
12 x 8 = 96
13 x 8 = 104
14 x 8 = 112
15 x 8 = 120
16 x 8 = 128
17 x 8 = 136
18 x 8 = 144
19 x 8 = 152
20 x 8 = 160
=========================
๐ค 0.1.2. One or More Parameters, No Return
Example without the return statement, often referred to as a procedure. In this case, the function implicitly returns the value None:
"""Displays the multiplication table of <base> from <start> to <end>."""
n = start
while n <= end:
print(n, 'x', base, '=', n * base, end=" ")
n += 1
table(7, 2, 11)
# 2 x 7 = 14 3 x 7 = 21 4 x 7 = 28 5 x 7 = 35 6 x 7 = 42
# 7 x 7 = 49 8 x 7 = 56 9 x 7 = 63 10 x 7 = 70 11 x 7 = 77
def table(base, start, end):
"""Displays the multiplication table of <base> from <start> to <end>."""
n = start
while n <= end:
print(n, 'x', base, '=', n * base, end=" ")
n += 1
table(7, 2, 11)
2 x 7 = 14 3 x 7 = 21 4 x 7 = 28 5 x 7 = 35 6 x 7 = 42 7 x 7 = 49 8 x 7 = 56 9 x 7 = 63 10 x 7 = 70 11 x 7 = 77
# Cool display
def table(base, start, end):
"""Displays the multiplication table of <base> from <start> to <end>."""
print(f"Multiplication Table for {base}:")
print(f"{'Number':<10}{'Result':<10}")
print("-" * 20)
for n in range(start, end + 1):
print(f"{n:<10}{n * base:<10}")
# Example call:
table(7, 2, 11)
Multiplication Table for 7:
Number Result
--------------------
2 14
3 21
4 28
5 35
6 42
7 49
8 56
9 63
10 70
11 77
๐ฅ One or more parameters, use of Return
Example with a single return statement:
"""
Calculate the square of a number.
Args:
x (float): The number to be squared.
Returns:
float: The square of the input number.
"""
return x**2
def squareArea(r):
"""
Calculate the area of a square given the length of its side.
Args:
r (float): The length of the side of the square.
Returns:
float: The area of the square.
"""
return square(r)
# Input for the side length and display of the area
side = float(input('Side: '))
print("Square area =", squareArea(side))
The return statement sends a value back to the caller and exits the function.
def square(x):
"""
Calculate the square of a number.
Args:
x (float): The number to be squared.
Returns:
float: The square of the input number.
"""
return x**2
def squareArea(r):
"""
Calculate the area of a square given the length of its side.
Args:
r (float): The length of the side of the square.
Returns:
float: The area of the square.
"""
return square(r)
# Input for the side length and display of the area
side = float(input('Side: '))
print("Square area =", squareArea(side))
Square area = 9.0
Example with multiple returns:
def surfaceVolumeSphere(r):
"""
Calculate the surface area and volume of a sphere.
Args:
r (float): The radius of the sphere.
Returns:
tuple: A tuple containing the surface area and volume of the sphere.
"""
surf = 4.0 * PI * r**2
vol = surf * r / 3
return surf, vol
radius = float(input('Radius: '))
s, v = surfaceVolumeSphere(radius)
print("Sphere with surface {:g} and volume {:g}".format(s, v))
Multiple values can be returned as a tuple and unpacked by the caller.
PI = 3.14
def surfaceVolumeSphere(r):
"""
Calculate the surface area and volume of a sphere.
Args:
r (float): The radius of the sphere.
Returns:
tuple: A tuple containing the surface area and volume of the sphere.
"""
surf = 4.0 * PI * r**2
vol = surf * r / 3
return surf, vol
radius = float(input('Radius: '))
s, v = surfaceVolumeSphere(radius)
print("Sphere with surface {:g} and volume {:g}".format(s, v))
Sphere with surface 200.96 and volume 267.947
๐ 0.2. Passing a Function as a Parameter
"""Display the values of <function>. Conditions: (lowerBound < upperBound) and (numSteps > 0)"""
h, x = (upperBound - lowerBound) / float(numSteps), lowerBound
while x <= upperBound:
y = function(x)
print("f({:.2f}) = {:.2f}".format(x, y))
x += h
return 2 * x**3 + x - 5
# f(-5.00) = -260.00
# f(-4.00) = -137.00
# ...
# f(5.00) = 250.00
Functions can be passed as arguments to other functions, enabling higher-order programming.
def tabulate(function, lowerBound, upperBound, numSteps):
"""Display the values of <function>. Conditions: (lowerBound < upperBound) and (numSteps > 0)"""
h, x = (upperBound - lowerBound) / float(numSteps), lowerBound
while x <= upperBound:
y = function(x)
print("f({:.2f}) = {:.2f}".format(x, y))
x += h
def myFunction(x):
return 2 * x**3 + x - 5
tabulate(myFunction, -5, 5, 10)
f(-5.00) = -260.00
f(-4.00) = -137.00
f(-3.00) = -62.00
f(-2.00) = -23.00
f(-1.00) = -8.00
f(0.00) = -5.00
f(1.00) = -2.00
f(2.00) = 13.00
f(3.00) = 52.00
f(4.00) = 127.00
f(5.00) = 250.00
๐ Improved Display and Explanation
Here's the improved version of the code with a more structured and readable output:
"""
Displays the values of <function> within the range from <lower_bound> to <upper_bound>.
The function is evaluated at evenly spaced points determined by <num_steps>.
Parameters:
function (callable): The function to be tabulated.
lower_bound (float): The starting point of the range.
upper_bound (float): The ending point of the range.
num_steps (int): The number of intervals in the range.
Conditions:
- lower_bound < upper_bound
- num_steps > 0
"""
step_size = (upper_bound - lower_bound) / float(num_steps)
x = lower_bound
print("Tabulation of the function from {:.2f} to {:.2f} with {} steps:".format(lower_bound, upper_bound, num_steps))
print("----------------------------------------------------------")
while x <= upper_bound:
y = function(x)
print("f({:+.2f}) = {:+.2f}".format(x, y))
x += step_size
def my_function(x):
"""A sample function: f(x) = 2x^3 + x - 5"""
return 2 * x**3 + x - 5
# Tabulate the function my_function from -5 to 5 with 10 steps
tabulate(my_function, -5, 5, 10)
----------------------------------------------------------
f(-5.00) = -260.00
f(-4.00) = -137.00
f(-3.00) = -68.00
f(-2.00) = -27.00
f(-1.00) = -8.00
f(+0.00) = -5.00
f(+1.00) = -2.00
f(+2.00) = +19.00
f(+3.00) = +82.00
f(+4.00) = +203.00
f(+5.00) = +395.00
๐ Explanation
- Function Parameters:
function: Takes a function as its value (e.g.,my_function)lower_bound: Starting point of the evaluation rangeupper_bound: Ending point of the evaluation rangenum_steps: Number of intervals between bounds
- Step Size Calculation:
- Calculated by dividing the range by number of steps
- Determines the increment for each iteration
- Looping and Evaluation:
- While loop iterates through the range
- Calculates function value at each point
- Prints formatted results
- Output Formatting:
- Values displayed with two decimal places
- Plus/minus signs for clear sign representation
- Structured header and separator lines
This structure makes it easy to understand how the function behaves over a specified range, providing clear and organized output.
def tabulate(function, lower_bound, upper_bound, num_steps):
"""
Displays the values of <function> within the range from <lower_bound> to <upper_bound>.
The function is evaluated at evenly spaced points determined by <num_steps>.
Parameters:
function (callable): The function to be tabulated.
lower_bound (float): The starting point of the range.
upper_bound (float): The ending point of the range.
num_steps (int): The number of intervals in the range.
Conditions:
- lower_bound < upper_bound
- num_steps > 0
"""
step_size = (upper_bound - lower_bound) / float(num_steps)
x = lower_bound
print("Tabulation of the function from {:.2f} to {:.2f} with {} steps:".format(lower_bound, upper_bound, num_steps))
print("----------------------------------------------------------")
while x <= upper_bound:
y = function(x)
print("f({:+.2f}) = {:+.2f}".format(x, y))
x += step_size
def my_function(x):
"""A sample function: f(x) = 2x^3 + x - 5"""
return 2 * x**3 + x - 5
# Tabulate the function my_function from -5 to 5 with 10 steps
tabulate(my_function, -5, 5, 10)
Tabulation of the function from -5.00 to 5.00 with 10 steps:
----------------------------------------------------------
f(-5.00) = -260.00
f(-4.00) = -137.00
f(-3.00) = -62.00
f(-2.00) = -23.00
f(-1.00) = -8.00
f(+0.00) = -5.00
f(+1.00) = -2.00
f(+2.00) = +13.00
f(+3.00) = +52.00
f(+4.00) = +127.00
f(+5.00) = +250.00
โก 0.3. Defining default values for function arguments
When defining a function, it is often recommended to set default values for certain arguments, especially optional ones. By defining default values for a function's arguments, it becomes possible to call the function with only some of the expected arguments. Here are some examples:
"""
Displays a greeting message with the given name and an optional title.
Parameters:
name (str): The name of the person to greet.
title (str, optional): The title of the person (default is 'Mr.').
Returns:
None
"""
print("Hello", title, name)
๐ก Explanation: The greeting function has two arguments: name and title. A default value ('Mr.') has been set for the title argument. Therefore, when the greeting function is called with only the name argument (omitting the title argument), the function will use the default value 'Mr.'.
def greeting(name, title='Mr.'):
"""
Displays a greeting message with the given name and an optional title.
Parameters:
name (str): The name of the person to greet.
title (str, optional): The title of the person (default is 'Monsieur').
Returns:
None
"""
print("Hello", title, name)
Example Usage:
# Output: Hello Mr. Smith
greeting('Smith')
# Output: Hello Mr. Smith
Bonjour Monsieur Dupont
๐ก Explanation: When the function is called with both arguments, the default value is overridden by the provided value.
Example:
# Output: Hello Ms. Smith
greeting('Smith', 'Ms.')
# Output: Hello Ms. Smith
Bonjour Mademoiselle Dupont
๐ก Explanation: By defining default values for a function's arguments, you can make the function calls more flexible, allowing it to be called with only a subset of the expected arguments when needed.
โ ๏ธ Note
Arguments without default values must be specified before arguments with default values. If this rule is not followed, Python will raise an error during execution. For example, the following function definition is incorrect:
"""
This function is incorrectly defined and will raise an error because the argument
with a default value ('title') comes before an argument without a default value ('name').
Parameters:
title (str, optional): The title of the person (default is 'Mr.').
name (str): The name of the person to greet.
Returns:
None
"""
๐ก Explanation: The greeting function is defined incorrectly because title, an argument with a default value, is placed before name, an argument without a default value. In Python, arguments without default values must precede those with default values to avoid a syntax error.
def greet(title='Mr.', name):
Cell In[16], line 1
def salutation(titre='Monsieur', name):
^
SyntaxError: parameter without a default follows parameter with a default
๐ Recursive Function
A function that calls itself to solve a problem by breaking it into smaller subproblems. It must have a base case to stop recursion.
if n <= 1: # base case
return 1
return n * fact(n-1) # recursive step
def fact(n):
if n <= 1: # base case
return 1
return n * fact(n-1) # recursive step
fact(3)
6
๐ข 1) Fibonacci Sequence
Defined by \(F(0)=0,\; F(1)=1,\; F(n)=F(n-1)+F(n-2)\) for \(n \ge 2\).
@lru_cache(None)
def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)
# first 10 terms
print([fib(i) for i in range(10)]) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
๐ 2) Geometric Sequence
Defined by \(a_0 = a0,\; a_n = r \cdot a_{n-1}\).
if n == 0:
return a0
return r * geom(n-1, a0, r)
# first 6 terms with a0=3, r=2: 3, 6, 12, 24, 48, 96
print([geom(i, a0=3, r=2) for i in range(6)])
๐ Notes: Always include a base case (n==0 or n<=1). In Python, deep recursion can hit the recursion limit; for large n, prefer iterative versions.
๐ฏ Recursive Functions (Why They Matter)
- Natural fit for self-similar problems: trees/graphs, nested folders, divide-and-conquer (binary search, mergesort, quicksort).
- Clear, math-like reasoning: mirrors inductive definitions; base case + recursive step make correctness easier to argue.
- Compact code for backtracking/combinatorics (paths, permutations, subsets).
- Trade-offs: extra call overhead, possible stack overflows; Python has no tail-call optimization, so prefer iterative versions for deep recursions or performance-critical paths.
ฮป 0.4. Lambda Functions
A lambda function is an anonymous function, meaning it is a function that consists of a block of instructions that can be called and reused like a regular function but without a name. Lambda functions are typically used for very short functions with few instructions, which do not require a full function definition using the def keyword.
The general syntax for defining a lambda function is as follows:
The example below illustrates the definition of a lambda function:
Note that even though a lambda function is not defined with a name, to retrieve the returned value when calling the function, you must assign it to a variable. The example below illustrates calling the previous lambda function with x=2 and y=3.
x(2,3)
x = lambda x, y : x * y
x(2,3)
# An advanced used of lambda function
def power(n):
return lambda x: x ** n # returns a function
square = power(2)
cube = power(3)
print(square(4)) # 16
print(cube(2)) # 8
16
8
# Pick the student with the highest average quiz score
students = [
{"name": "Alice", "quiz": [10, 8, 9]},
{"name": "Bob", "quiz": [7, 6, 9]},
{"name": "Cara", "quiz": [9, 10, 10]},
]
top_student = max(students, key=lambda d: sum(d["quiz"]) / len(d["quiz"]))
print(top_student["name"]) # Cara
Cara
ฮป Lambda Functions (Why They Matter)
- Pass behavior as data: tiny anonymous functions make higher-order patterns ergonomic (
sorted(key=...),min/max(key=...),map,filter, callbacks). - Concise, one-off logic without polluting the namespace with throwaway function names.
- Closures: can capture surrounding variables to parameterize behavior on the fly.
- Trade-offs: single-expression only; overuse can hurt readabilityโuse
defwhen logic grows or needs documentation/tests.
๐ Rule of thumb: use recursion when the problem is naturally recursive and depth is bounded; use lambda for short, local transformationsโotherwise write a named def for clarity.
๐ Variable Scope: Local vs Global
Note: When we define variables inside a function, these variables are only accessible within that function itself. These variables are referred to as ยซ local ยป to the function. However, when variables are defined outside of the function in the main program body, they are called ยซ global ยป variables. The content of a global variable is visible and accessible from within a function, but the function cannot modify the value of the variable.
p = 20
print(p, q)
p = 15
q = 38
print(p, q) # Outputs: 15 38
myFunction() # Calls the function, Outputs: 20 38
print(p, q) # Outputs: 15 38
def myFunction():
p = 20
print(p, q)
p = 15
q = 38
print(p, q) # Outputs: 15 38
myFunction() # Calls the function, Outputs: 20 38
print(p, q) # Outputs: 15 38
15 38
20 38
15 38
๐ Explanation
- Global and Local Variables:
- The variables
pandqare defined in the global scope, outside the functionmyFunction(). Hence, they are global variables. - Inside
myFunction(), a new variablepis defined with the value20. Thispis local to the function and does not affect the globalp.
- The variables
- Output Analysis:
- Before Function Call (
print(p, q)): This prints the global values ofpandq, which are15and38, respectively. - Inside Function (
myFunction()): WhenmyFunction()is called, it prints the localp(which is20) and the globalq(which remains38). The localpinside the function shadows the globalpbut does not change it. - After Function Call (
print(p, q)): This again prints the global values ofpandq, which are still15and38, as the localpinside the function did not alter the globalp.
- Before Function Call (
However, you can modify this default behavior by allowing the function to modify the value of a global variable. To do this, you must explicitly declare the variable as global within the function. Example:
global p
p = 20
print(p, q)
p = 15
q = 38
print(p, q) # Outputs: 15 38
myFunction() # Calls the function, Outputs: 20 38
print(p, q) # Outputs: 20 38
๐ Explanation
- Global Declaration:
- Inside
myFunction(), theglobalkeyword is used to indicate thatprefers to the global variablep, not a new local variable.
- Inside
- Output Analysis:
- Before Function Call (
print(p, q)): This prints the global values ofpandq, which are15and38, respectively. - Inside Function (
myFunction()): WhenmyFunction()is called, it sets the globalpto20and prints this new value ofpalong with the globalq(which remains38). - After Function Call (
print(p, q)): This prints the updated global values ofpandq, which are now20and38, respectively, reflecting the change made insidemyFunction().
- Before Function Call (
def myFunction():
global p
p = 20
print(p, q)
p = 15
q = 38
print(p, q) # Outputs: 15 38
myFunction() # Calls the function, Outputs: 20 38
print(p, q) # Outputs: 20 38
15 38
20 38
20 38
๐ฆ 0.5. Arbitrary number of arguments
๐ 0.5.0. Passing a Tuple
Use *args to pass a variable number of positional arguments as a tuple.
"""Returns the sum of the <tuple>."""
result = 0
for nombre in args:
result += nombre
return result
# Example calls:
print(total(23)) # 23
print(total(23, 42, 13)) # 78
def total(*args):
"""Returns the sum of the <tuple>."""
result = 0
for number in args:
result += number
return result
# Example calls:
print(total(23)) # 23
print(total(23, 42, 13)) # 78
23
78
If the function has multiple arguments, the tuple is in the last position. It is also possible to pass a tuple (or actually a sequence) to the call, which will be unpacked into a list of parameters for a "classic" function:
return a + b + c
# Example call:
elements = (2, 4, 6)
print(total(*elements)) # 12
def total(a, b, c):
return a + b + c
# Example call:
elements = (2, 4, 6)
print(total(*elements)) # 12
12
๐ 0.5.1. Passing a Dictionary
Use **kargs to pass a variable number of keyword arguments as a dictionary.
return kargs
# Examples of calls
## Using named parameters:
print(unDict(a=23, b=42)) # {'a': 23, 'b': 42}
## Providing a dictionary:
mots = {'d': 85, 'e': 14, 'f': 9}
print(unDict(**mots)) # {'d': 85, 'e': 14, 'f': 9}
def unDict(**kargs):
return kargs
# Examples of calls
## Using named parameters:
print(unDict(a=23, b=42)) # {'a': 23, 'b': 42}
## Providing a dictionary:
mots = {'d': 85, 'e': 14, 'f': 9}
print(unDict(**mots)) # {'d': 85, 'e': 14, 'f': 9}
{'a': 23, 'b': 42}
{'d': 85, 'e': 14, 'f': 9}
๐ 0.6. Documenting a Function
After creating a function (especially a relatively long and complex one), it is highly recommended to document it to allow other users to understand it quickly. Function documentation is typically a string that provides an overview of the function and useful details. This description is generally specified right after the function's name declaration and before the definition of other instruction blocks. The example below illustrates how to document a function and how to access this documentation when needed.
def volumeSphere():
""" This program calculates the volume of a sphere.
The function is defined with a single required argument r
which represents the radius of the sphere.
It can take any positive value."""
r = float(input("Enter the radius of the sphere: "))
PI = 3.14
return (4 * PI * r**3) / 3
In the definition of the volumeSphere function, the string does not play any functional role in the script; it is treated by Python as a simple comment but is stored as internal documentation for the function. This documentation is stored in an attribute called __doc__. To display this attribute, you use:
print(volumeSphere.__doc__)
This program calculates the volume of a sphere.
The function is defined with a single required argument r
which represents the radius of the sphere.
It can take any positive value.
๐ฆ 1. Modules
A Python program is generally composed of several source files, called modules. Their names have the .py suffix. If correctly coded, modules should be independent of each other and reusable on demand in other programs.
Modules are files that group sets of functions. A module is an independent file that allows a program to be split into several scripts. This mechanism allows for the efficient creation of function or class libraries.
Advantages of modules:
- Code reuse
- Documentation and tests can be integrated into the module
- Implementation of shared services or data
- Partitioning of the system's namespace
Just as dictionaries are collections of objects (lists, tuples, sets, etc.), modules are collections of functions that perform related tasks. For example, the math module contains a number of mathematical functions such as sine, cosine, tangent, square root, etc. Many modules are already pre-installed in Python's standard library. However, to perform certain specific tasks, you often need to install additional modules (e.g., numpy, scipy, matplotlib, pandas, etc.).
๐ฅ 1.0. Importing a Module
There are two possible syntaxes:
- The
import nom_modulecommand imports all objects from the module:import tkinter - The
from <nom_module> import obj1, obj2command imports only the specified objectsobj1, obj2...from the module:from math import pi, sin, log
It is recommended to import in the following order:
- Standard library modules
- Third-party library modules
- Personal modules
๐ 1.1. The Standard Library
It is often said that Python comes "batteries included" due to its standard library, which is rich with over 200 packages and modules designed to address a wide range of common problems. See The Python Standard Library.
import math
dir(math) # To see the list of functions and attributes in the module.
['__doc__',
'__loader__',
'__name__',
'__package__',
'__spec__',
'acos',
'acosh',
'asin',
'asinh',
'atan',
'atan2',
'atanh',
'cbrt',
'ceil',
'comb',
'copysign',
'cos',
'cosh',
'degrees',
'dist',
'e',
'erf',
'erfc',
'exp',
'exp2',
'expm1',
'fabs',
'factorial',
'floor',
'fma',
'fmod',
'frexp',
'fsum',
'gamma',
'gcd',
'hypot',
'inf',
'isclose',
'isfinite',
'isinf',
'isnan',
'isqrt',
'lcm',
'ldexp',
'lgamma',
'log',
'log10',
'log1p',
'log2',
'modf',
'nan',
'nextafter',
'perm',
'pi',
'pow',
'prod',
'radians',
'remainder',
'sin',
'sinh',
'sqrt',
'sumprod',
'tan',
'tanh',
'tau',
'trunc',
'ulp']
help(math.gamma) # Displays the documentation for the gamma function in the math module.
Help on built-in function gamma in module math:
gamma(x, /)
Gamma function at x.
from math import sin # Imports the sine function
from math import cos, sin, tan, pi # Imports the cosine, sine, tangent functions, and the value of pi (3.14)
from math import * # Imports all functions and constants from the math module (equivalent to import math)
๐ข Some uses of the math function:
v = 16 # defines a variable v
x = sqrt(v) # Returns the square root of v
y = exp(v) # Returns the exponential of v
z = log(v) # Returns the natural logarithm of v
from math import *
v = 16 # defines a variable v
x = sqrt(v) # Returns the square root of v
y = exp(v) # Returns the exponential of v
z = log(v) # Returns the natural logarithm of v
print(v, x, y, z)
๐ฒ Some examples of using the random module:
import random
x = random.random() # Returns a random number between 0.0 and 1.0
print(x)
0.8583860290511665
import random
x = random.randint(5, 17) # Returns a random integer between 5 and 17 (inclusive)
print(x)
8
import random
x = random.uniform(5, 17) # Returns a random floating-point number between 5 and 17
print(x)
8.888314572814354
๐ Explore These Modules
Feel free to explore the turtle, time, decimal, fractions, and cmath modules.
- ๐ข
turtle- Graphics and drawing - โฐ
time- Time-related functions - ๐ข
decimal- Decimal floating point arithmetic - ๐
fractions- Rational number arithmetic - โก
cmath- Mathematical functions for complex numbers
๐ 1.2. Third-Party Libraries
In addition to the modules included in the standard Python distribution, you can find libraries in various fields:
- ๐ฌ Scientific
- ๐๏ธ Databases
- ๐งช Functional testing and quality control
- ๐ฎ 3D
- ...
The PYPI (The Python Package Index) lists thousands of modules and packages!
๐ค 1.3. Define and Use Your Own Module
You can create your own module by gathering several functions into a single script and saving it with the .py extension in the current directory. The name should be simple and not create ambiguity with other Python objects. For example, you might choose myprogram.py.
Once the script is saved in the current directory, you can import the module like a standard module, and all its functions (and variables) become accessible. The module is imported using the command:
def greet(name):
"""Returns a greeting message."""
return f"Hello, {name}!"
def add(a, b):
"""Returns the sum of two numbers."""
return a + b
import myprogram
print(myprogram.greet("Alice")) # Output: Hello, Alice!
print(myprogram.add(5, 7)) # Output: 12
๐ ๏ธ Practical: Create and Use Your Own Module
๐ Step 1: Create the Module File
Open your preferred text editor and write the following code, which you should save as cube_m.py:
def cube(y):
"""Calculates the cube of the parameter <y>."""
return y**3
# Self-test ----------------------------------------------------
if __name__ == "__main__": # False when imported ==> ignored
help(cube)
# displays the docstring of the function
print("cube of 9:", cube(9)) # cube of 9: 729
๐ Step 2: Use the Module
Create another file and import the function cube() from cube_m.py:
from cube_m import cube
for i in range(1, 4):
print("cube of", i, "=", cube(i), end=" ")
# cube of 1 = 1 cube of 2 = 8 cube of 3 = 27
๐ 2. Package
A second level of organization allows for structuring the code: Python files can be organized in a directory hierarchy called a package.
More simply, a package is a module containing other modules. The modules in a package can be sub-packages, creating a tree-like structure. In summary, a package is simply a directory that contains modules and an __init__.py file describing the package's structure. Example:
๐ ๏ธ Practical: Create and Use a Package
๐ Step 1: Create the Package Structure
In a terminal, do the following:
cd monpackage
touch __init__.py
touch mesfonctions.py
touch mesattributs.py
mesfonctions.py - contains two Python functions:
return a + b
def soustraire(a, b):
return a - b
mesattributs.py - contains two constants:
y = 95
๐ Step 2: Use the Package
This sequence of code demonstrates how to use the functions and constants defined in the monpackage package:
1. Importing and Using Functions:
mesfonctions.additionner(23, 89) == 112 # Returns True
Here, the additionner function is imported from the mesfonctions module within the monpackage package. The function returns 112, which matches the expected result.
2. Importing and Using Constants:
mesattributs.x == 100 # Returns True
Here, the constant x is imported from the mesattributs module. The expression evaluates to True since the value of x is indeed 100.
from monpackage import mesfonctions
mesfonctions.additionner(23,89) == 112 # Returns True
from monpackage import mesattributs
mesattributs.x == 100 # Returns True
๐ฏ Key Takeaways
- Functions are defined with
def, can have default arguments, and return values withreturn. - Docstrings (
"""...""") document what a function does โ accessible viahelp(). - Modules are
.pyfiles containing functions; import them withimportorfrom ... import. - Packages are directories of modules with an
__init__.pyfile. - Functions can be passed as arguments to other functions, enabling powerful abstractions.
โ
โ