Functions#
A function is a reusable block of code defined with the def keyword. Calling it executes the code inside, achieving the “write once, use many times” effect.
Python functions support default parameters, keyword arguments, variadic parameters, and can be passed around and returned as first-class objects.
Defining and Calling Functions#
1
2
3
4
5
6
7
| # Define a function
def greet(name):
print(f"Hello, {name}!")
# Call the function
greet("Alice") # Hello, Alice!
greet("Bob") # Hello, Bob!
|
Return Values#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| def add(a, b):
return a + b
result = add(3, 5)
print(result) # 8
# Can return multiple values (actually returns a tuple)
def min_max(numbers):
return min(numbers), max(numbers)
lo, hi = min_max([3, 1, 4, 1, 5, 9])
print(lo, hi) # 1 9
# Without return, or return with no value, returns None
def do_nothing():
pass
result = do_nothing()
print(result) # None
|
Parameter Types#
Positional Arguments#
1
2
3
4
| def describe(name, age, city):
print(f"{name}, age {age}, lives in {city}")
describe("Alice", 25, "Taipei") # passed in order
|
Default Arguments#
1
2
3
4
5
6
| def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("Alice") # Hello, Alice!
greet("Bob", "Hi") # Hi, Bob!
greet("Charlie", greeting="Hey") # Hey, Charlie!
|
Note: Default parameters must come after non-default parameters.
Keyword Arguments#
1
2
3
4
5
| def describe(name, age, city):
print(f"{name}, age {age}, lives in {city}")
# Pass by keyword — order does not matter
describe(age=25, city="Taipei", name="Alice")
|
*args: Accept any number of positional arguments#
1
2
3
4
5
6
| def total(*args):
print(args) # received as a tuple
return sum(args)
print(total(1, 2, 3)) # 6
print(total(1, 2, 3, 4, 5)) # 15
|
**kwargs: Accept any number of keyword arguments#
1
2
3
4
5
6
7
8
9
| def print_info(**kwargs):
print(kwargs) # received as a dict
for key, value in kwargs.items():
print(f"{key}: {value}")
print_info(name="Alice", age=25, city="Taipei")
# name: Alice
# age: 25
# city: Taipei
|
Combining all parameter types#
1
2
3
4
5
6
7
| def flexible(required, *args, default="hello", **kwargs):
print(f"Required parameter: {required}")
print(f"Extra positional args: {args}")
print(f"Default parameter: {default}")
print(f"Extra keyword args: {kwargs}")
flexible("mandatory", 1, 2, 3, default="world", x=10, y=20)
|
Variable Scope#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| x = 10 # global variable
def foo():
x = 20 # local variable, does not affect the global x
print(x) # 20
foo()
print(x) # 10
# To modify a global variable inside a function, use global
def bar():
global x
x = 30
bar()
print(x) # 30
|
Lambda Functions (Anonymous Functions)#
A lambda is a concise single-line function, commonly used with sorted(), map(), and filter():
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| # Syntax: lambda parameters: expression
square = lambda x: x ** 2
print(square(5)) # 25
add = lambda a, b: a + b
print(add(3, 4)) # 7
# Use with sorted()
words = ["banana", "apple", "cherry", "kiwi"]
sorted_words = sorted(words, key=lambda w: len(w))
print(sorted_words) # ['kiwi', 'apple', 'banana', 'cherry']
# Sort a list of dicts with sorted()
students = [
{"name": "Alice", "score": 85},
{"name": "Bob", "score": 92},
{"name": "Charlie", "score": 78},
]
by_score = sorted(students, key=lambda s: s["score"], reverse=True)
for s in by_score:
print(f"{s['name']}: {s['score']}")
|
Common Higher-Order Functions#
A higher-order function is one that accepts another function as an argument. Lambda expressions are most commonly used as arguments to these functions:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# map(): apply a function to every element
squares = list(map(lambda x: x ** 2, numbers))
print(squares) # [1, 4, 9, 16, 25]
# filter(): keep elements that satisfy a condition
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # [2, 4, 6, 8, 10]
# sorted(): customise the sort key
words = ["banana", "apple", "cherry", "kiwi"]
by_length = sorted(words, key=lambda w: len(w))
print(by_length) # ['kiwi', 'apple', 'banana', 'cherry']
|
For the full coverage of map(), filter(), and sorted() — including multiple sequences, multi-key sorting, and advanced patterns — see CH11: Common Built-in Functions.
Closures#
A function can remember variables from its enclosing scope:
1
2
3
4
5
6
7
8
9
10
| def make_multiplier(factor):
def multiplier(x):
return x * factor # remembers factor
return multiplier
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
|
Recursion#
A function can call itself:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
print(factorial(5)) # 120 (5 × 4 × 3 × 2 × 1)
# Fibonacci sequence
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
for i in range(10):
print(fibonacci(i), end=" ")
# 0 1 1 2 3 5 8 13 21 34
|
Practical Examples#
Moving average#
1
2
3
4
5
6
7
8
9
10
11
| def moving_average(prices, window):
"""Calculate a moving average."""
result = []
for i in range(len(prices) - window + 1):
avg = sum(prices[i:i + window]) / window
result.append(round(avg, 2))
return result
prices = [100, 102, 98, 105, 110, 108, 112]
ma3 = moving_average(prices, 3)
print(ma3) # [100.0, 101.67, 104.33, 107.67, 110.0]
|
Quicksort#
1
2
3
4
5
6
7
8
9
10
11
| def quicksort(arr):
if len(arr) <= 1:
return arr
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quicksort(left) + middle + quicksort(right)
data = [3, 6, 8, 10, 1, 2, 1]
print(quicksort(data)) # [1, 1, 2, 3, 6, 8, 10]
|