Study guide
7.2 Lambda Expressions and Higher-Order Functions
OpenEDG PCAP — Certified Associate in Python Programming Exam
Track your progress as you read
Finished sections get a check mark.
1 Chapter 1: The PCAP-31-03 Exam Blueprint and Test-Day Logistics
1.1 The PCAP-31-03 Blueprint, Scoring, and Exam-Day Logistics
2 Chapter 2: Modules, Packages, and the Standard Library
2.1 Module Imports and Namespace Mechanics 2.2 Module Construction and Execution 2.3 Package Hierarchy and Navigation 2.4 Standard Library Modules and Pip
3 Chapter 3: Exceptions and Error Handling
3.1 Exception Control Flow and Syntax 3.2 The Built-in Exception Hierarchy 3.3 Raising Exceptions and Assertions 3.4 Custom Exceptions and Exception Chaining
4 Chapter 4: Strings, Encodings, and Text Processing
4.1 Character Encodings and String Immutability 4.2 String Indexing, Slicing, and Comparison 4.3 String Inspection and Classification Methods 4.4 String Transformation and Formatting Methods 4.5 Iterating, Sorting, and Comparing Strings Against Other Types
5 Chapter 5: Object-Oriented Programming Fundamentals
5.1 Classes, Instances, and Attributes 5.2 Encapsulation, Privacy, and Name Mangling 5.3 Dunder Methods and Object Lifecycle 5.4 Discovering Class Structure: __name__, __module__, __bases__, and Identity
6 Chapter 6: Advanced OOP, Inheritance, and Metaprogramming
6.1 Single Inheritance, Method Overriding, and Polymorphism 6.2 Multiple Inheritance and Method Resolution Order (MRO) 6.3 Class Methods, Static Methods, and Properties
7 Chapter 7: Functional Python, Comprehensions, and Closures
7.1 List, Dictionary, and Set Comprehensions 7.2 Lambda Expressions and Higher-Order Functions 7.3 Variable Scopes, the LEGB Rule, and Closures
8 Chapter 8: Iterators, Generators, and File I/O
8.1 Iterators and the Iteration Protocol 8.2 Generator Functions and Generator Expressions 8.3 File Input/Output and Context Managers 8.4 Predefined Streams, Handles vs. Streams, and errno Diagnostics
7.2 Lambda Expressions and Higher-Order Functions
Key Takeaways Explain This Topic AI
The lambda keyword defines anonymous, inline function objects evaluated dynamically at runtime using the syntax lambda arguments: expression.
Lambda bodies are restricted to a single syntactic expression; statements (return, raise, pass, assignments, loops) cannot be placed inside a lambda.
map(func, *iterables) and filter(func, iterable) return lazy iterators in Python 3 that compute transformed or filtered items on demand.
filter(None, iterable) removes all falsy values (0, False, empty strings, None, empty collections) using Python's native truthiness evaluation.
sorted(), list.sort(), min(), and max() accept a unary key callable parameter to perform custom, complex, and multi-criteria comparisons.
Last updated: August 2026
Lambda Expressions and Higher-Order Functions
In Python, functions are first-class citizens —they can be assigned to variables, passed as arguments to other functions, stored in data structures, and returned as values from functions. A function that accepts another function as an argument or returns a function is called a higher-order function . To support functional paradigms without cluttering namespaces with trivial helper functions, Python provides anonymous functions via the lambda keyword.
1. Anonymous Functions and the lambda Keyword
A lambda function (or lambda form) is an inline, un-named function defined at the point of expression evaluation.
Syntax lambda parameter1, parameter2, ...: expression # Standard named function
def square(x):
return x ** 2
- # Equivalent anonymous lambda function assigned to a variable
- sq = lambda x: x ** 2
- print(square(5)) # 25
- print(sq(5)) # 25
- print(type(sq)) # <class 'function'>
At runtime, Python treats a function created via def and a function created via lambda as instances of the exact same class: types.FunctionType . The only significant difference is that lambda functions have their __name__ attribute set to "<lambda>" instead of an explicit identifier.
2. Argument Support and Syntactic Restrictions
While lambdas can accept any standard Python argument pattern, their internal structure is strictly limited by the grammar of the language.
- Argument Varieties
- Lambda expressions support all parameter passing mechanisms available to standard functions: # No arguments
- get_pi = lambda: 3.14159
- print(get_pi()) # 3.14159
- # Multiple positional arguments
- add = lambda x, y: x + y
- print(add(10, 20)) # 30
- # Default parameter values
- greet = lambda name, prefix="Hello": f"{prefix}, {name}!"
print(greet("Alice")) # Hello, Alice!
print(greet("Bob", prefix="Hi")) # Hi, Bob!
- # Variable positional arguments (*args)
- sum_all = lambda *args: sum(args)
- print(sum_all(1, 2, 3, 4, 5)) # 15
- # Keyword arguments (**kwargs)
- format_kv = lambda **kwargs: ", ".join(f"{k}={v}" for k, v in kwargs.items())
- print(format_kv(host="localhost", port=8080)) # host=localhost, port=8080
- Rigid Syntactic Restrictions on the Lambda Body
On the PCAP exam, understanding what cannot be written inside a lambda is just as important as knowing what can:
Single Expression Only : The body of a lambda must contain exactly one syntactic expression. It cannot contain a suite of multiple expressions separated by semicolons or newlines.
Implicit Return : The result of evaluating the single expression is automatically returned. Writing an explicit return keyword inside a lambda raises a SyntaxError .
No Statements Allowed : Statements perform actions but do not evaluate to values. The following statements are strictly forbidden inside a lambda:
- Assignments: lambda x: x = x + 1 ( SyntaxError )
- Control flow statements: while , for , pass , break , continue ( SyntaxError )
- Exception statements: raise ValueError() , try , except ( SyntaxError )
- Assertions: assert x > 0 ( SyntaxError )
Conditional Expressions ARE Permitted : While an if statement is forbidden, Python's ternary conditional expression ( expr1 if cond else expr2 ) evaluates to a single value and is fully legal: # Legal: Ternary expression in lambda
- parity = lambda n: "Even" if n % 2 == 0 else "Odd"
- print(parity(7)) # Odd
- print(parity(8)) # Even
- 3. The map() Built-in Function
The built-in map() function applies a transformation function to every item of an iterable and yields the results.
Signature and Laziness map(function, iterable, *additional_iterables)
In Python 3, map() returns a lazy map iterator object ( <class 'map'> ), computing values only when requested rather than building an entire list in memory: numbers = [1, 2, 3, 4, 5]
mapped_iter = map(lambda x: x * 10, numbers)
- print(mapped_iter) # <map object at 0x...>
- print(next(mapped_iter)) # 10
- print(next(mapped_iter)) # 20
- # Convert remaining elements to a concrete list
- print(list(mapped_iter)) # [30, 40, 50]
- Multiple Iterables and the Shortest Termination Rule
If multiple iterables are supplied to map() , the mapping function must accept as many arguments as there are iterables. Iteration terminates as soon as the shortest iterable is exhausted: list_a = [1, 2, 3, 4, 5]
list_b = [10, 20, 30] # Length 3 (shortest)
- sums = list(map(lambda x, y: x + y, list_a, list_b))
- print(sums) # [11, 22, 33] (Elements 4 and 5 of list_a are ignored)
- 4. The filter() Built-in Function
The built-in filter() function constructs an iterator from elements of an iterable for which a predicate function returns a truthy value.
- Signature filter(function_or_None, iterable) values = [12, -4, 0, 25, -9, 30, -1]
- positives = list(filter(lambda x: x > 0, values))
- print(positives) # [12, 25, 30]
- The None Predicate Feature (Truthiness Filter)
If the first argument to filter() is None , Python uses the identity truth-testing function. It filters out all elements that evaluate to False in a boolean context ( 0 , 0.0 , "" , [] , {} , None , False ): mixed_data = [0, "hello", False, 42, "", None, [1, 2], {}, -5]
- truthy_items = list(filter(None, mixed_data))
- print(truthy_items) # ['hello', 42, [1, 2], -5]
- Comparing map() / filter() with Comprehensions
- Pattern Higher-Order Function Pipeline Equivalent List Comprehension
- Transform list(map(lambda x: x**2, seq)) [x**2 for x in seq]
- Filter list(filter(lambda x: x > 0, seq)) [x for x in seq if x > 0]
- Filter + Transform list(map(lambda x: x**2, filter(lambda x: x > 0, seq))) [x**2 for x in seq if x > 0]
In modern Python, list comprehensions are generally preferred for readability, but map() and filter() remain essential when working with existing named callables (e.g., map(int, string_list) or filter(str.isalpha, chars) ).
5. Writing Your Own Functions That Accept Lambdas
map() and filter() are not special: they simply declare a parameter and call it. Objective PCAP-31-03 5.2 explicitly covers self-defined functions taking lambdas as arguments , and
writing one requires no syntax you have not already seen — the parameter is an ordinary name that
happens to hold a callable. def apply_twice(operation, value):
- """Call `operation` on `value`, then on the result."""
- return operation(operation(value))
- print(apply_twice(lambda x: x * 3, 2)) # 18 -> (2*3)*3
- print(apply_twice(lambda s: s + "!", "hi")) # 'hi!!'
- A second common shape is a self-defined function that builds a result under a caller-supplied
rule, which is how key= parameters work internally: def summarize(items, transform, keep):
result = []
for item in items:
if keep(item):
- result.append(transform(item))
- return result
- readings = [-4, 7, 0, 12, -1]
- print(summarize(readings, lambda n: n * 100, lambda n: n > 0))
- # [700, 1200]
Two rules govern these designs:
A lambda is just a function object. type(lambda x: x) is <class 'function'> , so a
parameter holding a lambda can equally receive a def -defined function, a built-in such as abs , or a method reference such as str.upper .
Give the callable parameter a default when the operation is optional. def scale(seq, op=lambda x: x): lets callers omit the transformation entirely.
Exam Trap: apply_twice(lambda x: x * 3, 2) passes the lambda object ; writing apply_twice(lambda x: x * 3(2)) instead calls 3(2) inside the lambda body and raises TypeError: 'int' object is not callable the moment the lambda runs.
6. Custom Sorting with sorted() , list.sort() , min() , and max()
Python provides powerful sorting mechanisms through the built-in sorted() function and the mutable list.sort() method.
sorted() vs list.sort()
sorted(iterable, key=None, reverse=False) : Returns a new sorted list from any iterable; leaves the original iterable untouched.
list.sort(key=None, reverse=False) : Modifies the list in-place and returns None .
The key Parameter and Lambdas
The key parameter accepts a unary function (a function taking one argument) that extracts or computes a comparison key for each element. The elements are sorted based on the return values of this key function. # Sort strings by length
- words = ["elephant", "cat", "hippopotamus", "dog", "dolphin"]
- sorted_by_len = sorted(words, key=lambda w: len(w))
- print(sorted_by_len) # ['cat', 'dog', 'elephant', 'dolphin', 'hippopotamus']
- # Sort list of tuples by second element (e.g. price)
- items = [("Laptop", 1200), ("Mouse", 25), ("Monitor", 300), ("Keyboard", 75)]
- sorted_by_price = sorted(items, key=lambda item: item[1])
- print(sorted_by_price)
- # [('Mouse', 25), ('Keyboard', 75), ('Monitor', 300), ('Laptop', 1200)]
- Multi-Criteria Sorting via Tuples
When sorting requires multiple precedence rules (e.g. sort primarily by score descending, secondarily by student name ascending), the lambda can return a tuple: students = [
- {"name": "Charlie", "score": 88},
- {"name": "Alice", "score": 95},
- {"name": "Bob", "score": 88},
- {"name": "David", "score": 95}
- ]
- # Primary sort: score descending (-score), Secondary sort: name ascending (name)
- students_sorted = sorted(students, key=lambda s: (-s["score"], s["name"]))
- print(students_sorted)
- # [
- # {'name': 'Alice', 'score': 95},
- # {'name': 'David', 'score': 95},
- # {'name': 'Bob', 'score': 88},
- # {'name': 'Charlie', 'score': 88}
- # ]
- Custom Key with min() and max()
The min() and max() built-ins also accept the key parameter to identify extreme elements based on custom criteria: employees = [
- {"name": "Sarah", "salary": 95000},
- {"name": "John", "salary": 120000},
- {"name": "Emma", "salary": 82000}
- ]
- top_earner = max(employees, key=lambda emp: emp["salary"])
- lowest_earner = min(employees, key=lambda emp: emp["salary"])
- print("Top Earner:", top_earner["name"]) # Top Earner: John
- print("Lowest Earner:", lowest_earner["name"]) # Lowest Earner: Emma