3 - Functions, Comprehensions, and Functional Patterns — Topics & Learning Outcomes
Module Topics
Defining Functions
Introduces the syntax and mechanics of creating reusable functions in Python. Covers the def keyword, naming conventions, and the role of the return statement.
- The def Statement — Functions in Python are defined using the def keyword followed by a name and a parenthesized parameter list. The indented block beneath the signature forms the function body.
- The return Statement — The return statement sends a value back to the caller and immediately exits the function. A function without an explicit return statement implicitly returns None.
- Docstrings and Function Documentation — A docstring is a string literal placed as the first statement inside a function body and serves as built-in documentation. It is accessible at runtime via the function's __doc__ attribute or the help() function.
- Calling a Function — A function is invoked by writing its name followed by parentheses containing any required arguments. The interpreter jumps to the function body, executes it, and resumes at the call site with the returned value.
Parameters and Arguments
Explores the different ways values can be passed into functions, including positional, keyword, and default parameters. Establishes clear terminology between parameters and arguments.
- Positional Parameters — Positional parameters are matched to arguments in the order they appear in the function signature. The first argument maps to the first parameter, the second to the second, and so on.
- Keyword Arguments — Keyword arguments are passed by explicitly naming the parameter they correspond to, allowing arguments to be supplied in any order. This improves readability, especially in functions with many parameters.
- Default Parameter Values — A parameter can be assigned a default value in the function signature, making the argument optional at the call site. If the caller does not supply that argument, the default is used.
- Parameters vs. Arguments Terminology — Parameters are the variable names listed in the function definition, while arguments are the actual values passed when the function is called. Keeping this distinction clear helps in reading error messages and documentation.
Variable-Length Arguments: *args and **kwargs
Covers the special *args and **kwargs syntax that allows functions to accept an arbitrary number of positional or keyword arguments. Explains how these mechanisms support flexible and reusable API design.
- Collecting Positional Arguments with *args — Prefixing a parameter name with an asterisk (*) causes Python to collect all extra positional arguments into a tuple assigned to that parameter. By convention the name args is used, though any valid identifier works.
- Collecting Keyword Arguments with **kwargs — Prefixing a parameter name with two asterisks (**) causes Python to collect all extra keyword arguments into a dictionary. The keys are the argument names and the values are the supplied values.
- Combining *args and **kwargs — A function can declare both *args and **kwargs together to accept any combination of positional and keyword arguments. This pattern is common in decorators and generic wrapper functions.
- Unpacking Arguments at the Call Site — The * and ** operators can also be used at the call site to unpack a list or tuple into positional arguments and a dictionary into keyword arguments respectively. This is the inverse of the collection behavior in definitions.
Scope and Variable Visibility
Explains how Python resolves variable names using the LEGB rule and how the global and nonlocal keywords allow functions to modify variables in enclosing scopes.
- The LEGB Scope Rule — Python resolves names by searching four scopes in order: Local, Enclosing, Global, and Built-in. The first scope in which the name is found wins.
- Local Variables — Any name assigned inside a function is treated as local to that function by default, even if a global variable with the same name exists. Local variables do not affect the outer scope.
- The global Keyword — The global keyword declares that a name inside a function refers to a module-level variable rather than creating a new local one. This allows the function to read and modify the global variable.
- The nonlocal Keyword — The nonlocal keyword is used inside a nested function to refer to a variable in the nearest enclosing function scope that is not global. It enables inner functions to modify variables from their enclosing scope.
Recursion
Introduces recursion as a technique where a function calls itself to solve a problem by reducing it to smaller instances. Covers base cases, recursive cases, and practical considerations like stack depth.
- What Is Recursion? — Recursion is a programming technique in which a function calls itself as part of its own definition. It is a natural fit for problems that can be broken into smaller versions of the same problem.
- Base Cases — A base case is a condition under which the recursive function returns a result directly without making another recursive call. Every recursive function must have at least one base case to eventually terminate.
- Recursive Cases — The recursive case defines how the function calls itself with a modified argument that moves closer to the base case. The result of the recursive call is typically combined with additional computation to produce the answer.
- Recursion Depth and Limitations — Python enforces a maximum recursion depth, defaultable around 1000 calls, to prevent call stack overflow. Very deep recursion will raise a RecursionError even when the logic is correct.
Comprehensions
Teaches the concise comprehension syntax for building lists, dictionaries, and sets from iterables. Covers optional filtering conditions and nested comprehensions.
- List Comprehensions — A list comprehension provides a compact syntax for creating a new list by applying an expression to each item in an iterable. It replaces many common for-loop patterns with a single readable line.
- Dictionary Comprehensions — Dictionary comprehensions build a new dictionary by specifying a key-value expression iterated over a source iterable. They are the dict equivalent of list comprehensions.
- Set Comprehensions — Set comprehensions use curly braces without a colon to produce a set of unique values derived from an iterable. Duplicate expressions are automatically discarded because sets do not allow repeated elements.
- Filtering in Comprehensions — All three comprehension types support an optional if clause that acts as a filter, including only items for which the condition evaluates to True. Filtering replaces a nested if statement inside a for loop.
- Nested Comprehensions — Comprehensions can contain multiple for clauses to iterate over nested iterables, effectively flattening or combining data from multiple sequences. The leftmost for clause is the outermost loop.
Lambda Functions and Functional Patterns
Introduces anonymous lambda functions and the map and filter built-ins as tools for applying functional programming patterns in Python. Explains when to use these tools versus comprehensions or named functions.
- Lambda Functions — A lambda function is an anonymous function defined with the lambda keyword in a single expression. It is used when a small throwaway function is needed without the overhead of a full def statement.
- The map() Function — The built-in map() applies a function to every item in an iterable and returns a map object, a lazy iterator, that yields the transformed values. It is the functional equivalent of a transformation comprehension.
- The filter() Function — The built-in filter() applies a predicate function to each element of an iterable and returns only the elements for which the predicate returns True. It is the functional equivalent of a comprehension with an if clause.
- Combining map and filter — map() and filter() can be composed by nesting calls, applying filtering first and then transformation, or vice versa. This chains functional operations without creating intermediate lists.
- Choosing Between Comprehensions and Functional Tools — Python offers both comprehensions and map/filter as ways to transform and filter data. Choosing between them depends on readability, the existence of a named function, and team conventions.
Student Learning Outcomes
By the end of this module, students will be able to:
MO1
Construct Python functions using the def keyword, appropriate parameter types (positional, keyword, default, *args, and **kwargs), and explicit return statements to produce reusable, documented code
Level: ApplyType: CognitiveCourse mapping: CO2
MO2
Trace Python's LEGB scope rule to predict which value a variable name resolves to within nested and global function contexts
Level: AnalyzeType: CognitiveCourse mapping: CO2
MO3
Implement a recursive function by identifying the base case and recursive case for a given problem
Level: ApplyType: CognitiveCourse mapping: CO2
MO4
Produce list, dictionary, and set comprehensions — including filter clauses and nested iteration — to transform data from existing iterables
Level: ApplyType: CognitiveCourse mapping: CO1
MO5
Evaluate when to apply lambda functions, map(), or filter() versus comprehensions for a given data-transformation task based on readability and functional requirements
Level: EvaluateType: CognitiveCourse mapping: CO3
Course Outcomes (reference)
CO1Use variables, arrays and object structures appropriate to solve engineering problems.
CO2Write computer functions to meet specific calculation needs.
CO3Write computer programs to solve mathematical expressions and engineering problems