Functions Practice Questions
Practice Exercises - Python 3 Functions & Abstractions
Practice Exercises: Python 3 Functions
This practice guide is structured to reinforce the function design concepts covered in Step 1: Functions Complete Guide. Solving these exercises will help you master variable boundaries, argument packing/unpacking, closures, recursion, type annotations, and custom decorator engineering.
Guidelines for Effective Learning:
- Solve Individually First: Write out your own logical code before reviewing the provided answers.
- Execute Locally: Run each code block in your local terminal to see dynamic outputs.
- Analyze Scopes: For closure and decorator exercises, map out where the variables exist in memory (local vs enclosing vs global).
Practice Questions Index
- Level 1: Function Basics & Scope (Questions 1-10) — Basic definitions, parameters, default values, and local isolation boundaries.
- Level 2: Parameter Variations & Multiple Returns (Questions 11-20) — Multiple value packing, early exits,
*args,**kwargs, and LEGB scoping rules. - Level 3: Functional Abstractions & Lambdas (Questions 21-30) — Lambda expressions, custom sorting, map/filter/reduce, comprehensions, and higher-order functions.
- Level 4: Intermediate Projects & Recursion (Questions 31-40) — Recursive algorithms, custom closures, type hint annotations, and docstring guidelines.
- Level 5: Advanced Engineering & Decorators (Questions 41-50) — Decorators, generators, and integrated validation pipeline projects.
Level 1: Function Basics & Scope (Questions 1-10)
Question 1: Square Calculation
Task: Write a function square(n) that returns the square of a number.
Answer:
main.pyRunnabledef square(n): return n ** 2 print(square(5)) # Outputs: 25 print(square(-3)) # Outputs: 9
Question 2: Area of a Rectangle
Task: Write a function area_of_rectangle(length, width) that returns the area of a rectangle.
Answer:
main.pyRunnabledef area_of_rectangle(length, width): return length * width print(area_of_rectangle(5, 3)) # Outputs: 15
Question 3: Default Parameter Exponents
Task: Write a function power(base, exponent=2) that returns the base raised to the exponent. If the exponent is not passed, it should default to 2.
Answer:
main.pyRunnabledef power(base, exponent=2): return base ** exponent print(power(3)) # Outputs: 9 (default exponent 2) print(power(2, 10)) # Outputs: 1024
Question 4: Boolean Status Checker
Task: Write a function is_even(n) that returns True if n is even, and False otherwise.
Answer:
main.pyRunnabledef is_even(n): return n % 2 == 0 print(is_even(4)) # Outputs: True print(is_even(7)) # Outputs: False
Question 5: Optional Greeting Format
Task: Write a function greet(name, greeting="Hello") that returns a formatted greeting string.
Answer:
main.pyRunnabledef greet(name, greeting="Hello"): return f"{greeting}, {name}!" print(greet("Alice")) # Outputs: Hello, Alice! print(greet("Bob", greeting="Hi")) # Outputs: Hi, Bob!
Question 6: Character Frequency Count
Task: Write a function count_vowels(text) that returns the number of vowels (a, e, i, o, u) inside a text string.
Answer:
main.pyRunnabledef count_vowels(text): return sum(1 for c in text.lower() if c in 'aeiou') print(count_vowels("Hello World")) # Outputs: 3
Question 7: Cumulative List Addition
Task: Write a function sum_of_list(numbers) that takes a list of integers and returns their sum using a loop.
Answer:
main.pyRunnabledef sum_of_list(numbers): total = 0 for n in numbers: total += n return total print(sum_of_list([1, 2, 3, 4, 5])) # Outputs: 15
Question 8: Validating Age Limits
Task: Write a function is_valid_age(age) that returns True if age is between 0 and 120 (inclusive).
Answer:
main.pyRunnabledef is_valid_age(age): return 0 <= age <= 120 print(is_valid_age(25)) # Outputs: True print(is_valid_age(-5)) # Outputs: False
Question 9: Fahrenheit Conversion Calculator
Task: Write a function celsius_to_fahrenheit(celsius) that takes a Celsius float value, converts it using , and returns the result.
Answer:
main.pyRunnabledef celsius_to_fahrenheit(celsius): return (celsius * 1.8) + 32 print(celsius_to_fahrenheit(0)) # Outputs: 32.0 print(celsius_to_fahrenheit(100)) # Outputs: 212.0
Question 10: Fuel Efficiency Metric
Task: Write a function calculate_mpg(miles, gallons) that takes miles driven and gallons used and returns miles per gallon. If gallons is 0, return 0.0 to prevent division errors.
Answer:
main.pyRunnabledef calculate_mpg(miles, gallons): if gallons == 0: return 0.0 return miles / gallons print(calculate_mpg(300, 10)) # Outputs: 30.0 print(calculate_mpg(100, 0)) # Outputs: 0.0
Section Checkup
Level 2: Parameter Variations & Multiple Returns (Questions 11-20)
Question 11: Tuple Return packaging
Task: Write a function min_max(numbers) that inspects a list of numbers and returns both the minimum and maximum values as a Tuple.
Answer:
main.pyRunnabledef min_max(numbers): return min(numbers), max(numbers) low, high = min_max([3, 1, 9, 4, 7]) print("Low:", low, "| High:", high) # Outputs: Low: 1 | High: 9
Question 12: Arbitrary Positional Arguments (*args)
Task: Write a function total_amounts(*numbers) that accepts any number of positional numeric arguments and returns their sum.
Answer:
main.pyRunnabledef total_amounts(*numbers): return sum(numbers) print(total_amounts(1, 2, 3)) # Outputs: 6 print(total_amounts(10, 20, 30, 40)) # Outputs: 100
Question 13: Arbitrary Keyword Arguments (**kwargs)
Task: Write a function build_user_profile(user_id, **info) that accepts a required user ID and any number of keyword parameters, returning them together as a dictionary.
Answer:
main.pyRunnabledef build_user_profile(user_id, **info): profile = {"user_id": user_id} profile.update(info) return profile print(build_user_profile("S101", email="s101@school.edu", level=3)) ## Outputs: {'user_id': 'S101', 'email': 's101@school.edu', 'level': 3}
Question 14: Mixed Arguments Order
Task: Write a function place_order(item, *modifiers, **delivery_details) that captures pizza topping modifiers as a list and delivery details as a dictionary.
Answer:
main.pyRunnabledef place_order(item, *modifiers, **delivery_details): return { "item": item, "modifiers": list(modifiers), "delivery": delivery_details } order = place_order("Pizza", "extra_cheese", "mushrooms", address="Room 101", priority=True) print(order)
Question 15: Word Count Dictionary
Task: Write a function word_frequencies(words_list) that takes a list of strings and returns a dictionary mapping each unique word to its frequency count.
Answer:
main.pyRunnabledef word_frequencies(words_list): freq = {} for word in words_list: freq[word] = freq.get(word, 0) + 1 return freq print(word_frequencies(["A", "B", "A", "C", "A", "B"])) ## Outputs: {'A': 3, 'B': 2, 'C': 1}
Question 16: Parameter Unpacking (List)
Task: Given a function plot_coordinates(x, y, z), use the asterisk operator (*) to unpack a 3-element list coordinates = [10, 20, 30] into the function call.
Answer:
main.pyRunnabledef plot_coordinates(x, y, z): return f"Coordinate: X={x}, Y={y}, Z={z}" coordinates = [10, 20, 30] print(plot_coordinates(*coordinates)) # Outputs: Coordinate: X=10, Y=20, Z=30
Question 17: Parameter Unpacking (Dictionary)
Task: Given the function configure_server(host, port, debug=False), use the double asterisk operator (**) to unpack config = {"host": "127.0.0.1", "port": 80, "debug": True} into the function call.
Answer:
main.pyRunnabledef configure_server(host, port, debug=False): return f"Connecting to {host}:{port} (Debug: {debug})" config = {"host": "127.0.0.1", "port": 80, "debug": True} print(configure_server(**config))
Question 18: Local Variable Isolation
Task: Explain what prints in the console when this script runs, highlighting the scoping boundary:
main.pyRunnableactive_node = "GlobalNode" def assign_node(): active_node = "LocalNode" print("Inside:", active_node) assign_node() print("Outside:", active_node)
Answer:
"Inside: LocalNode"is printed first. Inside the function, the local assignmentactive_node = "LocalNode"creates a local variable that masks the global variable."Outside: GlobalNode"is printed second. The global variableactive_noderemains completely unchanged outside the local function scope.
Question 19: Global State modification
Task: Write a function reset_system_counter() that uses the global keyword to reset a global variable active_connections = 12 to 0.
Answer:
main.pyRunnableactive_connections = 12 def reset_system_counter(): global active_connections active_connections = 0 reset_system_counter() print("Global connections:", active_connections) # Outputs: 0
Question 20: Early Return Validation
Task: Write a function calculate_square_root(n) that checks if n is negative. If so, exit early returning None. Otherwise, return .
Answer:
main.pyRunnabledef calculate_square_root(n): if n < 0: return None # Exit early to prevent mathematical domain errors return n ** 0.5 print(calculate_square_root(25)) # Outputs: 5.0 print(calculate_square_root(-9)) # Outputs: None
Section Checkup
Level 3: Functional Abstractions & Lambdas (Questions 21-30)
Question 21: Tuple List Sorting using Lambda
Task: Sort a list of course coordinates [("Chemistry", 205), ("Calculus", 101), ("Physics", 103)] alphabetically by their classroom numbers (the second element of each tuple).
Answer:
main.pyRunnableclassrooms = [("Chemistry", 205), ("Calculus", 101), ("Physics", 103)] ## Sort by room number using lambda key sorted_rooms = sorted(classrooms, key=lambda room: room[1]) print("Sorted Classrooms:", sorted_rooms) ## Outputs: [('Calculus', 101), ('Physics', 103), ('Chemistry', 205)]
Question 22: Transform Lists with map()
Task: Use the map() function and a lambda expression to double every value in a list of integers [1, 2, 3, 4].
Answer:
main.pyRunnablenums = [1, 2, 3, 4] doubled = list(map(lambda x: x * 2, nums)) print("Doubled elements:", doubled) # Outputs: [2, 4, 6, 8]
Question 23: Filter Lists with filter()
Task: Use the filter() function and a lambda expression to keep only positive elements in a list [-5, 12, -3, 8, 0, -1].
Answer:
main.pyRunnablenums = [-5, 12, -3, 8, 0, -1] positives = list(filter(lambda x: x > 0, nums)) print("Positive elements:", positives) # Outputs: [12, 8]
Question 24: Aggregate Lists with reduce()
Task: Use functools.reduce and a lambda expression to calculate the combined product of elements in [1, 2, 3, 4].
Answer:
main.pyRunnablefrom functools import reduce nums = [1, 2, 3, 4] product = reduce(lambda a, b: a * b, nums) print("Product aggregate:", product) # Outputs: 24
Question 25: List Comprehensions
Task: Recode the mapping operation from Question 22 (doubling list values) using a list comprehension instead of map().
Answer:
main.pyRunnablenums = [1, 2, 3, 4] doubled = [x * 2 for x in nums] print("Comprehended list:", doubled) # Outputs: [2, 4, 6, 8]
Question 26: Dictionary Comprehensions
Task: Given a dictionary scores = {"Alice": 85, "Bob": 92}, use a dictionary comprehension to convert each score into a float out of 1.0 (e.g. 0.85).
Answer:
main.pyRunnablescores = {"Alice": 85, "Bob": 92} decimal_ratios = {name: score / 100.0 for name, score in scores.items()} print("Decimal ratios:", decimal_ratios) ## Outputs: {'Alice': 0.85, 'Bob': 0.92}
Question 27: Set Comprehensions
Task: Given a raw list of student assignments ["Art", "Science", "Art", "Math", "Science"], use a set comprehension to deduplicate and make all string values lowercase.
Answer:
main.pyRunnableassignments = ["Art", "Science", "Art", "Math", "Science"] unique_lowercase = {subj.lower() for subj in assignments} print("Deduplicated lowercase set:", unique_lowercase) ## Outputs: {'art', 'science', 'math'}
Question 28: Higher-Order Function Arguments
Task: Write a higher-order function execute_utility(func, value) that accepts a function and a value, applying the function to the value and returning the result.
Answer:
main.pyRunnabledef execute_utility(func, value): return func(value) print(execute_utility(str.lower, "ALERT")) # Outputs: alert print(execute_utility(lambda x: x * 3, 10)) # Outputs: 30
Question 29: Closure (Multiplier Factory)
Task: Write a closure multiplier_factory(factor) that returns an inner function that multiplies any number passed to it by the predefined factor.
Answer:
main.pyRunnabledef multiplier_factory(factor): def multiply(x): return x * factor return multiply double = multiplier_factory(2) triple = multiplier_factory(3) print("Double 8:", double(8)) # Outputs: 16 print("Triple 8:", triple(8)) # Outputs: 24
Question 30: Closure State Tracker (Accumulator)
Task: Write a closure make_accumulator() that returns an inner function. The inner function maintains a running total state across separate calls using the nonlocal keyword.
Answer:
main.pyRunnabledef make_accumulator(): running_total = 0.0 def accumulate(value): nonlocal running_total running_total += value return running_total return accumulate acc = make_accumulator() print(acc(10)) # Outputs: 10.0 print(acc(45)) # Outputs: 55.0 print(acc(-5)) # Outputs: 50.0
Section Checkup
Level 4: Intermediate Projects & Recursion (Questions 31-40)
Question 31: Recursion — Factorials
Task: Write a recursive function factorial(n) that calculates .
Answer:
main.pyRunnabledef factorial(n): if n <= 1: return 1 # Base case: terminates recursion return n * factorial(n - 1) # Recursive case print(factorial(5)) # Outputs: 120
Question 32: Recursion — Fibonacci Sequence
Task: Write a recursive function fibonacci(n) that returns the -th number in the Fibonacci sequence ().
Answer:
main.pyRunnabledef fibonacci(n): if n <= 0: return 0 if n == 1: return 1 return fibonacci(n - 1) + fibonacci(n - 2) print(fibonacci(6)) # Outputs: 8 (positions: 0=0, 1=1, 2=1, 3=2, 4=3, 5=5, 6=8)
Question 33: Recursion — Sum of Digits
Task: Write a recursive function sum_digits(n) that sums all digits of a positive integer (e.g. 123 ➡️ ).
Answer:
main.pyRunnabledef sum_digits(n): if n < 10: return n # Base case return (n % 10) + sum_digits(n // 10) # Add last digit to recursive sum of the rest print(sum_digits(12345)) # Outputs: 15
Question 34: Recursion — Flattening Lists
Task: Write a recursive function flatten(nested_list) that flattens a nested list structure of any depth (e.g., [1, [2, 3]] ➡️ [1, 2, 3]).
Answer:
main.pyRunnabledef flatten(nested_list): flat_list = [] for item in nested_list: if isinstance(item, list): flat_list.extend(flatten(item)) # Recursively flatten sublist else: flat_list.append(item) return flat_list print(flatten([1, [2, 3], [4, [5, 6]]])) # Outputs: [1, 2, 3, 4, 5, 6]
Question 35: Dispatch Calculator (No Classes)
Task: Build a calculator dispatcher system using a dictionary mapping string keys ("+", "-", "*") to corresponding operations, executing calculations procedurally.
Answer:
main.pyRunnabledef add(a, b): return a + b def sub(a, b): return a - b def mul(a, b): return a * b ## Dispatch Table operations = {"+": add, "-": sub, "*": mul} def dispatch_calc(val1, operator, val2): if operator in operations: return operations[operator](val1, val2) return None print(dispatch_calc(10, "+", 5)) # Outputs: 15 print(dispatch_calc(10, "*", 3)) # Outputs: 30
Question 36: Partial Arguments (functools.partial)
Task: Use functools.partial to create a multiply_by_five function from an existing multiply(a, b) function.
Answer:
main.pyRunnablefrom functools import partial def multiply(a, b): return a * b ## Create a specialized function with 'a' pre-filled as 5 multiply_by_five = partial(multiply, 5) print(multiply_by_five(10)) # Outputs: 50 print(multiply_by_five(4)) # Outputs: 20
Question 37: Enclosing scope Logging Closure
Task: Write a closure create_system_logger(module_name) that returns an inner function that logs formatted messages prefixed by the module_name context.
Answer:
main.pyRunnabledef create_system_logger(module_name): def log(message): print(f"[{module_name.upper()}] {message}") return log auth_log = create_system_logger("Authentication") db_log = create_system_logger("Database") auth_log("User login verified.") # Outputs: [AUTHENTICATION] User login verified. db_log("Record successfully updated.") # Outputs: [DATABASE] Record successfully updated.
Question 38: Type Hint Annotations
Task: Add type hints to a function process_averages(names_list, scores_list) to declare that it expects a list of strings and a list of floats, and returns a list of float results.
Answer:
main.pyRunnabledef process_averages(names_list: list[str], scores_list: list[float]) -> list[float]: adjusted_scores = [] for score in scores_list: adjusted_scores.append(score + 1.5) return adjusted_scores
Question 39: Docstring Documentation
Task: Write a function calculate_circle_area(radius) that calculates circle area (). Document it using a complete, professional docstring that details parameters, return types, and potential exceptions.
Answer:
main.pyRunnabledef calculate_circle_area(radius: float) -> float: """ Calculate the total area of a circle based on its radius. Args: radius: Positive float representing the circle's radius. Returns: The calculated floating-point area value. Raises: ValueError: If the provided radius is negative. """ if radius < 0.0: raise ValueError("Radius value cannot be negative.") return 3.1415926535 * (radius ** 2)
Question 40: Custom Iterator Generators (yield)
Task: Write a generator function generate_multiples_of_ten(limit) that lazily yields multiples of 10 up to the specified limit without loading them all into memory at once.
Answer:
main.pyRunnabledef generate_multiples_of_ten(limit): n = 10 while n <= limit: yield n n += 10 ## Access values lazily for multiple in generate_multiples_of_ten(40): print("Yielded:", multiple)
Output:
Yielded: 10 Yielded: 20 Yielded: 30 Yielded: 40
Section Checkup
Level 5: Advanced Engineering & Decorators (Questions 41-50)
Question 41: Basic Decorator — Action Logger
Task: Write a decorator @log_activity that prints "Executing function..." and the target function's name before it runs.
Answer:
main.pyRunnabledef log_activity(func): def wrapper(*args, **kwargs): print(f"Executing function: {func.__name__}...") return func(*args, **kwargs) return wrapper @log_activity def check_status(): return "Status: Operational" print(check_status())
Output:
Executing function: check_status... Status: Operational
Question 42: Performance Timer Decorator
Task: Write a decorator @measure_duration that measures how many seconds a function takes to execute, printing the elapsed duration.
Answer:
main.pyRunnableimport time def measure_duration(func): def wrapper(*args, **kwargs): start_time = time.perf_counter() result = func(*args, **kwargs) elapsed = time.perf_counter() - start_time print(f"Function {func.__name__} executed in {elapsed:.6f} seconds.") return result return wrapper @measure_duration def calculate_sum(limit): return sum(range(limit)) calculate_sum(1000000)
Question 43: Memoization Caching Decorator
Task: Write a decorator @cache_results that stores calculated inputs and outcomes in a local dictionary, retrieving cached values immediately on duplicate calls.
Answer:
main.pyRunnabledef cache_results(func): cache = {} def wrapper(*args): if args not in cache: cache[args] = func(*args) # Compute and store if not cached return cache[args] return wrapper @cache_results def compute_fibonacci(n): if n < 2: return n return compute_fibonacci(n - 1) + compute_fibonacci(n - 2) print(compute_fibonacci(40)) # Instant calculation using cache
Question 44: Rate Limiter Delay Decorator
Task: Write a decorator @rate_limit_delay(seconds) that introduces a mandatory delay (using time.sleep) of the specified duration before calling the function.
Answer:
main.pyRunnableimport time def rate_limit_delay(seconds): def decorator(func): def wrapper(*args, **kwargs): print(f"Introducing safety buffer: delaying execution for {seconds} seconds...") time.sleep(seconds) return func(*args, **kwargs) return wrapper return decorator @rate_limit_delay(1.5) def send_api_request(): return "API response: 200 OK" print(send_api_request())
Question 45: Exception Retry Decorator
Task: Write a decorator @retry_on_failure(attempts) that catches exceptions raised by a function, retrying execution up to the specified number of attempts before propagating the error.
Answer:
main.pyRunnabledef retry_on_failure(attempts): def decorator(func): def wrapper(*args, **kwargs): last_error = None for attempt in range(1, attempts + 1): try: return func(*args, **kwargs) except Exception as e: last_error = e print(f"Attempt #{attempt} failed with error: {e}") raise last_error return wrapper return decorator @retry_on_failure(attempts=3) def process_unstable_service(): raise ConnectionError("Timeout error: database unreachable.") ## process_unstable_service() # Will attempt 3 times and then raise ConnectionError
Question 46: Stacking Multiple Decorators
Task: Stack @measure_duration and @log_activity on a single function, and explain what sequence they execute in.
Answer:
main.pyRunnable## Stacking decorators @measure_duration @log_activity def compute_calculations(): return sum(range(500000)) compute_calculations()
Execution Sequence:
@measure_durationis the outermost wrapper, so it starts measuring the time first.- It calls the wrapper for
@log_activity. @log_activitylogs"Executing function..."and calls the actualcompute_calculationsfunction.- Once completed,
@measure_durationprints the elapsed time and returns the calculated result.
Question 47: Type Enforcement Decorator
Task: Write a decorator @enforce_types(expected_type) that inspects the first argument passed to a function, raising a TypeError if it is not of the expected_type.
Answer:
main.pyRunnabledef enforce_types(expected_type): def decorator(func): def wrapper(*args, **kwargs): if args and not isinstance(args[0], expected_type): raise TypeError(f"First argument must be of type {expected_type.__name__}.") return func(*args, **kwargs) return wrapper return decorator @enforce_types(str) def register_username(username): return f"Username registered: {username}" print(register_username("MarcusVance")) # OK ## register_username(1052) # Raises TypeError: First argument must be of type str.
Question 48: Authentication Verification Decorator
Task: Write a decorator @require_admin_role that verifies if a global dictionary active_session = {"user": "Alice", "role": "user"} has "admin" clearance role. If not, raise a PermissionError.
Answer:
main.pyRunnableactive_session = {"user": "Alice", "role": "user"} def require_admin_role(func): def wrapper(*args, **kwargs): if active_session.get("role") != "admin": raise PermissionError("Access Denied: Administrative role clearance required.") return func(*args, **kwargs) return wrapper @require_admin_role def wipe_system_logs(): return "Database transaction: logs cleared." ## active_session["role"] = "admin" # Set to admin to bypass error ## wipe_system_logs() # Raises PermissionError unless role is modified
Question 49: Functional Processing Pipeline
Task: Write a function generate_pipeline(*funcs) that accepts any number of transformation functions as positional arguments, returning a single, unified composite function that processes values in sequence.
Answer:
main.pyRunnabledef generate_pipeline(*funcs): def execute_pipeline(initial_value): value = initial_value for func in funcs: value = func(value) return value return execute_pipeline ## Pipeline transformations clean_text_pipeline = generate_pipeline( str.strip, str.lower, lambda s: s.replace(" ", "_") ) print(clean_text_pipeline(" SYSTEM TRANSACTION COMPLETED ")) ## Outputs: system_transaction_completed
Question 50: Custom Infinite Step Generator
Task: Write a generator function infinite_step_generator(start=0, step=1) that lazily yields a count that increments by step indefinitely, and use next() to verify its functionality.
Answer:
main.pyRunnabledef infinite_step_generator(start=0, step=1): current = start while True: yield current current += step counter = infinite_step_generator(start=10, step=5) print(next(counter)) # Outputs: 10 print(next(counter)) # Outputs: 15 print(next(counter)) # Outputs: 20
Congratulations! 🎓
You have completed all 50 practice questions for Python Functions & Abstractions! You have mastered parameters, default arguments, closures, recursion, type annotations, generators, and decorator systems. Keep up the great work!
Finished this lesson?
Mark it complete to advance your curriculum progress.