Chapter 4 — Functions, Modules, and Scope
Functions let you encapsulate and reuse logic. This chapter covers defining and calling functions, arguments, scope, modules, lambdas, and a brief introduction to recursion — the organizational backbone of larger data-science programs.
Learning Objectives
- Define and call functions with parameters and return values.
- Use positional, keyword, default, and
*args/**kwargsarguments. - Understand mutable vs immutable argument passing.
- Explain variable scope (local, global, nonlocal).
- Import and create modules.
- Use lambda functions and
map/filter. - Apply top-down stepwise refinement.
- Write simple recursive functions.
Prerequisites / Imports
Uses the standard statistics and math modules.
import statistics
import math
1 Defining and Calling Functions
A function packages a block of code under a name. Use def; return a value with return.
def greet(name):
return f'Hello, {name}!'
print(greet('Data Scientist'))
Hello, Data Scientist!
2 Return Values and Multiple Returns
A function may return nothing (returns None) or several values as a tuple.
def min_max(values):
return min(values), max(values)
lo, hi = min_max([3, 1, 4, 1, 5, 9])
print('min:', lo, 'max:', hi)
min: 1 max: 9
3 Argument Flavors
Positional, keyword, defaults, and variadic *args/**kwargs.
def describe(name, role='analyst', *skills, **meta):
print(f'{name} - {role}')
print('skills:', skills)
print('meta:', meta)
describe('Ada', 'engineer', 'python','sql', team='data', level=3)
Ada - engineer
skills: ('python', 'sql')
meta: {'team': 'data', 'level': 3}
4 Mutable vs Immutable Arguments
Python passes object references. Mutating a mutable argument inside a function affects the caller; rebinding the name does not.
def add_item(lst, item):
lst.append(item) # mutates the shared list
nums = [1, 2, 3]
add_item(nums, 4)
print(nums)
[1, 2, 3, 4]
def rebind(lst):
lst = [99] # rebinds local name only
nums = [1, 2, 3]
rebind(nums)
print(nums)
[1, 2, 3]
5 Scope: Local, Global, nonlocal
Names follow the LEGB rule (Local, Enclosing, Global, Built-in). Use global/nonlocal sparingly.
counter = 0
def increment():
global counter
counter += 1
increment(); increment()
print('counter:', counter)
counter: 2
6 Modules
A module is a .py file of reusable code. Import with import or from ... import. You can create your own module by writing a .py file and importing it.
# Create a tiny module file on disk, then import it
with open('mymods.py', 'w') as f:
f.write('def circle_area(r):\n return 3.141592653589793 * r * r\n')
import mymods
print('Area of r=5:', round(mymods.circle_area(5), 2))
Area of r=5: 78.54
7 Lambda Functions and map/filter
Lambdas are anonymous one-expression functions, handy with map and filter.
square = lambda x: x * x
print(square(6))
nums = [1, 2, 3, 4, 5]
print('squared:', list(map(lambda x: x**2, nums)))
print('evens:', list(filter(lambda x: x % 2 == 0, nums)))
36 squared: [1, 4, 9, 16, 25] evens: [2, 4]
8 Top-Down Stepwise Refinement
Break a big problem into functions, then refine each. Example: a small statistics summary.
def summarize(data):
return {
'count': len(data),
'mean': statistics.mean(data),
'median': statistics.median(data),
'stdev': statistics.stdev(data) if len(data) > 1 else 0.0,
'min': min(data),
'max': max(data),
}
scores = [78, 85, 90, 62, 71, 88, 95, 80]
summarize(scores)
{'count': 8, 'mean': 81.125, 'median': 82.5, 'stdev': 10.776131031126154, 'min': 62, 'max': 95}9 A Brief Look at Recursion
A function that calls itself needs a base case. Factorial is the canonical example.
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
print('5! =', factorial(5))
print('math.factorial(5) =', math.factorial(5))
5! = 120 math.factorial(5) = 120
Case Study: A Reusable Data Summary Toolkit
Combine the helpers into a small toolkit and apply it to a synthetic dataset.
import random
random.seed(7)
scores = [random.randint(50, 100) for _ in range(30)]
def summarize(data):
return {
'count': len(data),
'mean': round(statistics.mean(data), 2),
'median': statistics.median(data),
'stdev': round(statistics.stdev(data), 2),
'min': min(data),
'max': max(data),
}
report = summarize(scores)
for k, v in report.items():
print(f'{k:>8}: {v}')
count: 30
mean: 69.2
median: 67.5
stdev: 14.17
min: 52
max: 91
Exercises
- Write a function
is_prime(n)returningTrue/False. - Write
celsius_to_fahrenheit(c)and its inverse. - Write a function that returns both the mean and median of a list.
- Demonstrate the difference between mutating and rebinding a list argument.
- Create a module
statskit.pywith asummarizefunction and import it. - Use
mapwith a lambda to double each value in a list. - Write a recursive
power(base, exp). - Write a function with a default argument and call it two ways.
- Explain why modifying a global from inside a function needs
global. - Refactor a 20-line script into three well-named functions.
Python Data Science: From Foundations to Applications — Chapter 4