Master Python user-defined functions (UDF) with this curated collection of 15 hands-on practice problems covering syntax, parameters, variable scope, closures, lambda expressions, and recursion.
Structured Learning Problem Set: Each exercise features an interactive collapsible solution toggle, syntax-highlighted code, dedicated terminal output, and technical architectural notes. Deepen your understanding of local and global variable scope, recursive algorithms, anonymous lambda functions, and Python's built-in functions.
def greet(name='Guest'):
print(f'Hello, {name}!')
greet('Alex')
greet()
Terminal Output:
Hello, Alex!
Hello, Guest!
def describe_pet(animal, name):
print(f'I have a {animal} named {name}.')
# Positional arguments
describe_pet('dog', 'Buddy')
# Keyword arguments (order independent)
describe_pet(name='Whiskers', animal='cat')
Terminal Output:
I have a dog named Buddy.
I have a cat named Whiskers.
def sum_all(*numbers):
total = 0
for n in numbers:
total += n
return total
print('Sum 3 items: ', sum_all(10, 20, 30))
print('Sum 5 items: ', sum_all(1, 2, 3, 4, 5))
Terminal Output:
Sum 3 items: 60
Sum 5 items: 15
def build_profile(first, last, **user_info):
profile = {'first_name': first, 'last_name': last}
profile.update(user_info)
return profile
user = build_profile('Alex', 'Ronald', role='Admin', dept='IT')
print(user)
Terminal Output:
{'first_name': 'Alex', 'last_name': 'Ronald', 'role': 'Admin', 'dept': 'IT'}
def get_min_max(numbers):
return min(numbers), max(numbers)
low, high = get_min_max([15, 3, 89, 42, 7])
print('Lowest: ', low)
print('Highest:', high)
Terminal Output:
Lowest: 3
Highest: 89
def find_first_even(numbers):
for num in numbers:
if num % 2 == 0:
return num # Early exit
return None
print('First even:', find_first_even([1, 3, 7, 8, 11]))
print('No even: ', find_first_even([1, 3, 5]))
Terminal Output:
First even: 8
No even: None
def remove_negatives(numbers):
return [x for x in numbers if x >= 0]
raw_data = [10, -5, 20, -1, 30]
clean_data = remove_negatives(raw_data)
print('Original untouched:', raw_data)
print('Cleaned output: ', clean_data)
Terminal Output:
Original untouched: [10, -5, 20, -1, 30]
Cleaned output: [10, 20, 30]
def calculate_bmi(weight_kg: float, height_m: float) -> float:
"""Calculates Body Mass Index (BMI) using weight and height."""
return round(weight_kg / (height_m ** 2), 2)
bmi = calculate_bmi(70.0, 1.75)
print('BMI Value: ', bmi)
print('Docstring: ', calculate_bmi.__doc__)
Terminal Output:
BMI Value: 22.86
Docstring: Calculates Body Mass Index (BMI) using weight and height.
x = 100 # Global variable
def shadow_demo():
x = 200 # Local variable shadowing global x
print('Inside function: ', x)
shadow_demo()
print('Outside function:', x)
Terminal Output:
Inside function: 200
Outside function: 100
counter = 0
def increment():
global counter
counter += 1
increment()
increment()
print('Global counter:', counter)
Terminal Output:
Global counter: 2
def make_counter():
count = 0
def step():
nonlocal count
count += 1
return count
return step
c = make_counter()
print('Step 1:', c())
print('Step 2:', c())
Terminal Output:
Step 1: 1
Step 2: 2
# Problem: lst=[] retains state across separate calls!
# Fix: use default None and initialize inside
def safe_append(item, target_list=None):
if target_list is None:
target_list = []
target_list.append(item)
return target_list
print('Call 1:', safe_append('A'))
print('Call 2:', safe_append('B'))
Terminal Output:
Call 1: ['A']
Call 2: ['B']
def factorial(n):
if n <= 1: # Base case
return 1
return n * factorial(n - 1) # Recursive step
print('Factorial 5:', factorial(5))
print('Factorial 0:', factorial(0))
Terminal Output:
Factorial 5: 120
Factorial 0: 1
students = [('Alex', 82), ('Sarah', 95), ('Ronald', 74), ('John', 88)]
# Sort ascending by score (item[1])
students.sort(key=lambda s: s[1])
print('Sorted by score:', students)
Terminal Output:
Sorted by score: [('Ronald', 74), ('Alex', 82), ('John', 88), ('Sarah', 95)]
def flatten(nested_list):
flat = []
for item in nested_list:
if isinstance(item, list):
flat.extend(flatten(item)) # Recurse
else:
flat.append(item)
return flat
matrix = [1, [2, [3, 4], 5], [6, 7]]
print('Flattened list:', flatten(matrix))
Terminal Output:
Flattened list: [1, 2, 3, 4, 5, 6, 7]
Work through all 15 practice questions, explore variable scoping, and execute recursion traces in our interactive companion Jupyter Notebook:
Explore interactive code cells for all 15 function challenges, test default parameter behaviors, inspect closures, and run self-validating assertions.
View on GitHub Run in Google Colab*args collects arbitrary extra positional arguments as a tuple, while **kwargs collects extra keyword arguments as a dictionary.
Default argument expressions are evaluated once at function definition time, not upon each call. Reusing a mutable default shares the same list across invocations. Always default to None instead.
global binds a variable name to the top-level module scope, while nonlocal binds to the nearest enclosing non-global scope (useful in nested functions and closures).
Use lambda for short, single-expression anonymous callbacks (such as key extractors in sorted(), min(), or max()). For complex logic, use standard def functions.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.