Skip to content

Python Lambda

Junaid Atari edited this page Jul 4, 2026 · 1 revision

What is a Lambda?

In Python, a lambda function is a small, anonymous (unnamed) function. While standard functions are defined using the def keyword, lambda functions are defined using the lambda keyword.

The Golden Rule of Lambdas

A lambda function can take any number of arguments, but can only have ONE expression.

Syntax

lambda arguments: expression

Deep Understanding: def vs lambda

Let's visualize the difference in how Python treats these under the hood.

flowchart TD
    subgraph Standard Function
    A[def keyword] --> B[Function Name]
    B --> C[Arguments]
    C --> D[Multiple Statements / Block]
    D --> E[Return Statement]
    end

    subgraph Lambda Function
    F[lambda keyword] --> G[Anonymous / No Name]
    G --> H[Arguments]
    H --> I[Single Expression]
    I --> J[Implicit Return]
    end
Loading

Code Example: The Basics

# Standard function
def add_standard(x, y):
    return x + y

# Lambda function
add_lambda = lambda x, y: x + y

print(add_standard(5, 3))  # Output: 8
print(add_lambda(5, 3))    # Output: 8

Note for Junaid: While assigning a lambda to a variable (like add_lambda) works, PEP 8 (Python's style guide) discourages it. Lambdas are meant to be used inline or passed as arguments.


Module 2: Supercharging with map() and filter() (Beginner -> Intermediate)

Lambdas truly shine when paired with higher-order functions like map() and filter().

1. The map() Function

map() applies a given function to every item of an iterable (like a list or dict) and returns an iterator (a generator-like object) yielding the results.

Syntax: map(function, iterable)

2. The filter() Function

filter() constructs an iterator from elements of an iterable for which a function returns True.

Syntax: filter(function, iterable)

Visualizing the Pipeline

flowchart LR
    A[Raw Iterable] --> B{filter: Keep if True}
    B -->|Discard| C[X]
    B -->|Keep| D[Filtered Iterable]
    D --> E[map: Transform each item]
    E --> F[Final Transformed Iterable]
Loading

Code Example: Lists and Dicts

products = [
    {"name": "Laptop", "price": 1200, "in_stock": True},
    {"name": "Mouse", "price": 25, "in_stock": False},
    {"name": "Keyboard", "price": 75, "in_stock": True},
    {"name": "Monitor", "price": 300, "in_stock": True}
]

# 1. FILTER: Get only in-stock products
# Lambda checks the 'in_stock' key in the dictionary
in_stock_items = filter(lambda p: p["in_stock"], products)

# 2. MAP: Apply a 10% discount to the filtered items
# Lambda calculates the new price and returns a NEW dictionary
discounted_items = map(
    lambda p: {"name": p["name"], "price": p["price"] * 0.90}, 
    in_stock_items
)

# Because map and filter return iterators, we must cast to list to see the data
final_list = list(discounted_items)

for item in final_list:
    print(item)
# Output: 
# {'name': 'Laptop', 'price': 1080.0}
# {'name': 'Keyboard', 'price': 67.5}
# {'name': 'Monitor', 'price': 270.0}

Module 3: Generators and Lambdas (Intermediate)

What is a Generator?

A generator is a function that returns an iterator using the yield keyword. It evaluates lazily, meaning it generates one item at a time, saving massive amounts of memory compared to creating a full list in memory.

Crucial Concept: In Python 3, map() and filter() actually return generators (iterators)!

Using Lambdas Inside Generators

We can create custom generator functions that utilize lambdas for internal logic, or we can yield lambda functions themselves.

Code Example: The Lambda Generator

# A generator that yields lambda functions based on a multiplier
def create_multipliers(n):
    for i in range(1, n + 1):
        # Yielding a lambda function that captures 'i'
        # Note: We use a default argument x=i to avoid late-binding closure issues
        yield lambda x, i=i: x * i

# Using the generator
multipliers_gen = create_multipliers(3)

for func in multipliers_gen:
    print(func(10)) 
    # Output: 10, 20, 30

Generator Expressions vs Map/Filter

While map and filter are great, Pythonic developers often prefer Generator Expressions for readability.

numbers = [1, 2, 3, 4, 5]

# Map/Filter approach (Returns a generator)
gen_map = map(lambda x: x**2, filter(lambda x: x % 2 != 0, numbers))

# Generator Expression approach (More Pythonic, highly recommended)
gen_expr = (x**2 for x in numbers if x % 2 != 0)

# Both consume the same memory and yield the same results!
print(list(gen_map))   # [1, 9, 25]
print(list(gen_expr))  # [1, 9, 25]

Module 4: The "Huge" Real-World Pipeline (Intermediate)

Let's combine everything: list, dict, map, filter, and custom generators to process a massive dataset.

Scenario: Junaid, you are building a backend for a logistics company. You receive a massive list of shipment dictionaries. You need to:

  1. Filter out delayed or lost shipments.
  2. Calculate the shipping cost based on weight and distance.
  3. Group the valid shipments by region using a generator to handle memory efficiently.
  4. Output a final dictionary mapping regions to lists of processed shipments.
import random

# 1. GENERATE MOCK DATA (List of Dicts)
regions = ["North", "South", "East", "West"]
statuses = ["delivered", "in_transit", "delayed", "lost"]

shipments = [
    {
        "id": f"SHIP-{i:04d}",
        "region": random.choice(regions),
        "weight_kg": round(random.uniform(1.0, 50.0), 2),
        "distance_km": random.randint(100, 5000),
        "status": random.choice(statuses)
    }
    for i in range(1000) # 1000 shipments
]

# 2. THE PROCESSING PIPELINE

# Step A: Filter out bad statuses using filter() and lambda
valid_shipments = filter(
    lambda s: s["status"] in ["delivered", "in_transit"], 
    shipments
)

# Step B: Map to calculate costs and format data using map() and lambda
# Cost formula: Base $10 + ($0.05 per kg) + ($0.01 per km)
processed_shipments = map(
    lambda s: {
        "id": s["id"],
        "region": s["region"],
        "cost": round(10 + (s["weight_kg"] * 0.05) + (s["distance_km"] * 0.01), 2),
        "status": s["status"]
    },
    valid_shipments
)

# Step C: Custom Generator to group by region
def group_by_region(shipment_iterator):
    """Generator that yields (region, list_of_shipments) tuples."""
    region_dict = {}
    for shipment in shipment_iterator:
        region = shipment["region"]
        if region not in region_dict:
            region_dict[region] = []
        region_dict[region].append(shipment)
    
    # Yield the grouped data one region at a time (Memory efficient!)
    for region, ship_list in region_dict.items():
        yield region, ship_list

# Step D: Execute the pipeline
# We pass the map iterator directly into our custom generator
grouped_generator = group_by_region(processed_shipments)

# Convert the generator output into a final Dict of Lists
final_report = {region: ship_list for region, ship_list in grouped_generator}

# 3. RESULTS
print(f"Total Regions Processed: {len(final_report)}")
for region, ships in final_report.items():
    total_revenue = sum(s["cost"] for s in ships)
    print(f"Region: {region} | Valid Shipments: {len(ships)} | Total Revenue: ${total_revenue:,.2f}")

Why this is powerful:

  1. Memory Efficiency: filter and map don't create intermediate lists. They pass data one by one to group_by_region.
  2. Readability: The logic for filtering and mapping is isolated in single-line lambdas.
  3. Scalability: If shipments had 10 million rows, this script would still run without crashing your RAM, thanks to the generator pipeline.

Module 5: Key Points & Cheat Sheet

🚨 Key Points for Junaid

  1. Implicit Return: Lambdas automatically return the result of the expression. You cannot use return inside a lambda.
  2. Single Expression: You cannot use statements like if/else blocks, for loops, or variable assignments inside a lambda. (You can use the ternary operator a if condition else b).
  3. Python 3 Iterators: map() and filter() return iterators. If you need to iterate over them twice, you must cast them to a list() first.
  4. PEP 8 Rule: Never assign a lambda to a variable name (e.g., add = lambda x, y: x + y). Use def add(x, y): return x + y instead. Lambdas are for throwaway, inline functions.

📋 Lambda & Functional Cheat Sheet

Concept Syntax / Example Description
Lambda lambda x, y: x + y Anonymous function, single expression.
Ternary in Lambda lambda x: "Even" if x%2==0 else "Odd" Inline if/else inside a lambda.
Map map(lambda x: x*2, my_list) Applies function to all items. Returns iterator.
Filter filter(lambda x: x>0, my_list) Keeps items where function returns True.
Dict Map {k: func(v) for k, v in my_dict.items()} Dict comprehension (often preferred over map for dicts).
Generator Func def gen(): yield lambda x: x*2 Function that yields lambdas or uses yield.
Gen Expression (x*2 for x in my_list if x>0) Inline generator. Replaces map/filter combos.

Module 6: Knowledge Quiz

Test your understanding, Junaid! (Answers are at the bottom).

Q1. What is the output of the following code?

x = [1, 2, 3]
y = map(lambda a: a**2, filter(lambda b: b%2!=0, x))
print(list(y))

A) [1, 4, 9] B) [1, 9] C) [2] D) Error

Q2. Which of the following is considered un-Pythonic according to PEP 8? A) sorted(users, key=lambda u: u['age']) B) square = lambda x: x * x C) list(filter(lambda x: x > 0, nums)) D) max(data, key=lambda item: item['score'])

Q3. True or False: In Python 3, calling map() immediately computes the results and stores them in a new list in memory.

Q4. How do you write a lambda that takes a dictionary d and returns the value of key "a" if it exists, otherwise returns 0? A) lambda d: d.get("a", 0) B) lambda d: return d["a"] if "a" in d else 0 C) lambda d: d["a"] or 0 D) Both A and C

Q5. What is the primary advantage of using a Generator Expression (x for x in data) over a List Comprehension [x for x in data]? A) It runs faster. B) It uses significantly less memory. C) It allows multiple expressions. D) It automatically filters out None values.


Module 7: Practice Exercises

Put your skills to the test! Try to solve these without looking at the solutions below.

Exercise 1: The Temperature Converter (Beginner)

You have a list of temperatures in Celsius: celsius = [0, 10, 20, 30, 40]. Use map() and a lambda to convert them to Fahrenheit. (Formula: F = C * 9/5 + 32)

Exercise 2: Dictionary Value Extraction (Beginner)

Given a dictionary of users and their ages: users = {"Alice": 25, "Bob": 17, "Charlie": 30, "Diana": 15} Use filter() and a lambda to find the names of users who are 18 or older. (Hint: filter the .items() or .keys()).

Exercise 3: The Custom Generator (Intermediate)

Write a generator function called power_sequence(base). It should yield lambda functions. The first lambda should calculate base**1, the second base**2, up to base**5. Iterate through the generator and print the result of calling each lambda with the number 2.

Exercise 4: Complex Data Pipeline (Intermediate)

You have a list of dictionaries representing books:

books = [
    {"title": "Dune", "genre": "Sci-Fi", "pages": 412, "rating": 4.8},
    {"title": "1984", "genre": "Dystopian", "pages": 328, "rating": 4.7},
    {"title": "The Hobbit", "genre": "Fantasy", "pages": 310, "rating": 4.9},
    {"title": "Neuromancer", "genre": "Sci-Fi", "pages": 271, "rating": 4.2}
]

Create a pipeline that:

  1. Filters for books with a rating >= 4.5.
  2. Maps the filtered books to a new dictionary format: {"title": title, "category": genre, "is_long": True/False} (where is_long is True if pages > 300).
  3. Converts the final result into a list.

Exercise 5: Memory-Efficient Chunking (Advanced)

Write a generator function chunk_generator(iterable, chunk_size) that takes any iterable (like a massive list) and yields chunks (lists) of size chunk_size. Then, use map and a lambda to apply this generator to a list of 100 numbers, yielding chunks of 15. Print the chunks.



🗝️ Solutions to Exercises

Solution 1:

celsius = [0, 10, 20, 30, 40]
fahrenheit = list(map(lambda c: (c * 9/5) + 32, celsius))
print(fahrenheit) # [32.0, 50.0, 68.0, 86.0, 104.0]

Solution 2:

users = {"Alice": 25, "Bob": 17, "Charlie": 30, "Diana": 15}
# Filter the keys based on the value in the dictionary
adults = list(filter(lambda name: users[name] >= 18, users.keys()))
print(adults) # ['Alice', 'Charlie']

Solution 3:

def power_sequence(base):
    for i in range(1, 6):
        # Yield a lambda. We use default arg i=i to capture the current loop value
        yield lambda x, i=i: x ** i

gen = power_sequence(3)
for func in gen:
    print(func(2)) 
    # 2**1 = 2
    # 2**2 = 4
    # 2**3 = 8
    # 2**4 = 16
    # 2**5 = 32

Solution 4:

books = [ ... ] # (Assume books list is defined)

# 1. Filter
high_rated = filter(lambda b: b["rating"] >= 4.5, books)

# 2. Map
transformed = map(
    lambda b: {
        "title": b["title"], 
        "category": b["genre"], 
        "is_long": b["pages"] > 300
    }, 
    high_rated
)

# 3. Cast to list
final_books = list(transformed)
print(final_books)

Solution 5:

def chunk_generator(iterable, chunk_size):
    chunk = []
    for item in iterable:
        chunk.append(item)
        if len(chunk) == chunk_size:
            yield chunk
            chunk = []
    if chunk: # Yield any remaining items
        yield chunk

data = range(100) # 0 to 99
# Map applies the chunk_generator to the data. 
# Note: map passes the whole iterable to the function once.
chunks_iter = map(lambda x: chunk_generator(x, 15), [data])

for chunk_list in chunks_iter:
    for c in chunk_list:
        print(c)

(Alternative cleaner way for Ex 5 without map: just call chunk_generator(data, 15) directly, but this demonstrates how to pass an iterable through a map/lambda structure!)


Quiz Answers

Q1: B ([1, 9]) - Filter keeps 1, 3. Map squares them to 1, 9.
Q2: B - Assigning a lambda to a variable (square = ...) violates PEP 8. Use def.
Q3: False - In Python 3, map and filter return lazy iterators (generators).
Q4: D - Both A and C work. d.get("a", 0) is standard, and d["a"] or 0 works if you are sure "a" exists or you want 0 for falsy values (though get is safer). Correction for strictness: A is the safest and most Pythonic, but C technically evaluates to 0 if "a" is missing and throws KeyError, wait, if "a" is missing d["a"] throws KeyError. So A is the only strictly correct answer without try/except.
Correct Answer: A
Q5: B - Generator expressions evaluate lazily, yielding one item at a time, saving massive amounts of RAM.