Intermediate Python Software Development
Functional Programming Tools
Smarter Ways to Work with Lists
You've likely written many for loops to process items in a list. Loops are fundamental, but for common tasks like transforming or filtering data, Python offers more expressive and efficient tools. These tools are inspired by a programming style called functional programming, which treats computation as the evaluation of mathematical functions and avoids changing state or mutable data.
Instead of telling the computer how to loop through a list step-by-step, you can simply declare what you want to do to the entire collection. This leads to cleaner, more readable code.
Quick, Anonymous Functions
Often, you need a simple function for a single, brief task, like squaring a number inside another operation. Defining a full function with def feels like overkill. This is where lambda expressions shine. A lambda is a small, anonymous function defined without a name.
lambda arguments: expression
It can have any number of arguments but only one expression, which is evaluated and returned. For example, a function that adds 10 to a number can be written concisely:
# A regular function
def add_ten(x):
return x + 10
# The lambda equivalent
add_ten_lambda = lambda x: x + 10
print(add_ten(5)) # Output: 15
print(add_ten_lambda(5)) # Output: 15
The real power of lambda functions comes when you use them inside other functions, particularly map() and filter().
Applying Functions to Collections
Python has built-in functions designed for applying operations across sequences. They allow you to transform and filter data without writing explicit loops.
The
map()function executes a specified function for each item in an iterable. The result is a map object, which is an iterator containing the transformed items.
Let's say we have a list of numbers and we want to square each one. Using map() with a lambda makes this a one-liner.
numbers = [1, 2, 3, 4, 5]
squared_numbers = map(lambda x: x * x, numbers)
# The result is a map object, so we convert it to a list to see the contents
print(list(squared_numbers)) # Output: [1, 4, 9, 16, 25]
Similarly, the filter() function creates an iterator from elements of an iterable for which a function returns True. It's a clean way to select items based on a condition.
numbers = [1, 2, 3, 4, 5, 6]
even_numbers = filter(lambda x: x % 2 == 0, numbers)
print(list(even_numbers)) # Output: [2, 4, 6]
For more complex aggregations, we can use reduce(). This function, which lives in the functools module, applies a rolling computation to a sequence. It takes a function and a list, then returns a single cumulative value. For instance, to find the product of all numbers in a list:
from functools import reduce
numbers = [1, 2, 3, 4]
product = reduce(lambda x, y: x * y, numbers)
print(product) # Output: 24
The lambda here takes two arguments: x is the accumulated value (like a running total), and y is the next item from the list. reduce iterates through the list, continuously updating the accumulator.
The Pythonic Alternative: Comprehensions
While map and filter are powerful, many Python developers prefer a more direct and often more readable syntax called comprehensions. A list comprehension provides a concise way to create lists, combining the logic of a for loop and an optional if condition into a single line.
[expression for item in iterable if condition]
Let's revisit our map and filter examples. Here's how you'd write them with list comprehensions:
numbers = [1, 2, 3, 4, 5]
# Map example: square every number
squared_numbers = [x * x for x in numbers]
print(squared_numbers) # Output: [1, 4, 9, 16, 25]
# Filter example: get even numbers
all_numbers = [1, 2, 3, 4, 5, 6]
even_numbers = [x for x in all_numbers if x % 2 == 0]
print(even_numbers) # Output: [2, 4, 6]
The syntax feels more like natural language: "give me x for each x in the list if it's even." This clarity makes comprehensions very popular.
The comprehension syntax isn't limited to lists. You can also create sets and dictionaries this way.
A set comprehension is similar to a list comprehension, but it returns a set, automatically handling duplicate values.
words = ["apple", "banana", "apple", "cherry"]
# Get the length of each unique word
word_lengths = {len(word) for word in words}
print(word_lengths) # Output: {5, 6} (order may vary)
A dictionary comprehension lets you build dictionaries from iterables. You specify both the key and the value in the expression.
# Create a dictionary mapping numbers to their squares
squares_dict = {x: x * x for x in range(1, 6)}
print(squares_dict) # Output: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
What is a core principle of the functional programming style described in the text?
Which line of code correctly uses the map() function to create a new list where each number from my_list is squared?
These functional tools and comprehensions help you write code that is not just more compact, but also more descriptive of your intent. They are a cornerstone of writing clean, efficient, and 'Pythonic' code.