Lambda functions for simple operations like getting an attribute or adding numbers add visual noise and reduce readability. The operator module provides efficient, named functions for built-in operators, making sorting keys and functional operations clearer and faster.

Comparison Operations

Function versions of comparison operators:

itemgetter.py
"""operator.itemgetter examples"""

import operator

# Basic itemgetter
print("Basic itemgetter:")

# Get single item
get_first = operator.itemgetter(0)
numbers = 

print(f"get_first({numbers}): {get_first(numbers)}")

# Get by key
data = {'name': 'Alice', 'age': 30}
get_name = operator.itemgetter('name')
print(f"get_name({data}): {get_name(data)}")

# Multiple items
print("\nMultiple items:")

# Get multiple indices
get_items = operator.itemgetter(0, 2, 4)
letters = ['a', 'b', 'c', 'd', 'e']

result = get_items(letters)
print(f"get_items({letters}): {result}")

# Multiple keys
get_info = operator.itemgetter('name', 'age')
person = {'name': 'Bob', 'age': 25, 'city': 'NYC'}
print(f"get_info: {get_info(person)}")

# Sorting lists
print("\nSorting lists:")

students = [
    {'name': 'Charlie', 'grade': 85},
    {'name': 'Alice', 'grade': 92},
    {'name': 'Bob', 'grade': 78}
]

# Sort by grade
sorted_by_grade = sorted(students, key=operator.itemgetter('grade'))
print("Sorted by grade:")
for s in sorted_by_grade:
    print(f"  {s}")

# Sort by name
sorted_by_name = sorted(students, key=operator.itemgetter('name'))
print("\nSorted by name:")
for s in sorted_by_name:
    print(f"  {s}")

# Nested sorting
print("\nNested sorting:")

data = [
    {'name': 'Alice', 'scores': [85, 90]},
    {'name': 'Bob', 'scores': [92, 88]},
    {'name': 'Charlie', 'scores': [78, 95]}
]

# Sort by first score
sorted_data = sorted(data, 
    key=lambda x: operator.itemgetter(0)(x['scores']))

print("Sorted by first score:")
for d in sorted_data:
    print(f"  {d}")

# With tuples
print("\nWith tuples:")

records = [
    ('Alice', 30, 'NYC'),
    ('Bob', 25, 'LA'),
    ('Charlie', 35, 'Chicago')
]

# Sort by age (index 1)
sorted_by_age = sorted(records, key=operator.itemgetter(1))
print("Sorted by age:")
for r in sorted_by_age:
    print(f"  {r}")

# Sort by multiple fields (city, then age)
sorted_multi = sorted(records, key=operator.itemgetter(2, 1))
print("\nSorted by city then age:")
for r in sorted_multi:
    print(f"  {r}")

# Map and filter
print("\nMap and filter:")

users = [
    {'id': 1, 'name': 'Alice', 'active': True},
    {'id': 2, 'name': 'Bob', 'active': False},
    {'id': 3, 'name': 'Charlie', 'active': True}
]

# Extract all names
names = list(map(operator.itemgetter('name'), users))
print(f"Names: {names}")

# Extract ids of active users
active_ids = [
    operator.itemgetter('id')(user)
    for user in users
    if user['active']
]
print(f"Active IDs: {active_ids}")

# Min/max
print("\nMin/max:")

products = [
    {'name': 'Widget', 'price': 29.99},
    {'name': 'Gadget', 'price': 49.99},
    {'name': 'Tool', 'price': 19.99}
]

cheapest = min(products, key=operator.itemgetter('price'))
print(f"Cheapest: {cheapest}")

most_expensive = max(products, key=operator.itemgetter('price'))
print(f"Most expensive: {most_expensive}")

# Grouping
print("\nGrouping:")

from itertools import groupby

transactions = [
    {'category': 'food', 'amount': 50},
    {'category': 'transport', 'amount': 20},
    {'category': 'food', 'amount': 30},
    {'category': 'transport', 'amount': 15}
]

# Must sort first for groupby
transactions.sort(key=operator.itemgetter('category'))

for category, items in groupby(transactions, 
                               key=operator.itemgetter('category')):
    total = sum(operator.itemgetter('amount')(item) for item in items)
    print(f"{category}: ${total}")

"""operator.itemgetter examples"""

import operator

# Basic itemgetter
print("Basic itemgetter:")

# Get single item
get_first = operator.itemgetter(0)
numbers = 

print(f"get_first({numbers}): {get_first(numbers)}")

# Get by key
data = {'name': 'Alice', 'age': 30}
get_name = operator.itemgetter('name')
print(f"get_name({data}): {get_name(data)}")

# Multiple items
print("\nMultiple items:")

# Get multiple indices
get_items = operator.itemgetter(0, 2, 4)
letters = ['a', 'b', 'c', 'd', 'e']

result = get_items(letters)
print(f"get_items({letters}): {result}")

# Multiple keys
get_info = operator.itemgetter('name', 'age')
person = {'name': 'Bob', 'age': 25, 'city': 'NYC'}
print(f"get_info: {get_info(person)}")

# Sorting lists
print("\nSorting lists:")

students = [
    {'name': 'Charlie', 'grade': 85},
    {'name': 'Alice', 'grade': 92},
    {'name': 'Bob', 'grade': 78}
]

# Sort by grade
sorted_by_grade = sorted(students, key=operator.itemgetter('grade'))
print("Sorted by grade:")
for s in sorted_by_grade:
    print(f"  {s}")

# Sort by name
sorted_by_name = sorted(students, key=operator.itemgetter('name'))
print("\nSorted by name:")
for s in sorted_by_name:
    print(f"  {s}")

# Nested sorting
print("\nNested sorting:")

data = [
    {'name': 'Alice', 'scores': [85, 90]},
    {'name': 'Bob', 'scores': [92, 88]},
    {'name': 'Charlie', 'scores': [78, 95]}
]

# Sort by first score
sorted_data = sorted(data, 
    key=lambda x: operator.itemgetter(0)(x['scores']))

print("Sorted by first score:")
for d in sorted_data:
    print(f"  {d}")

# With tuples
print("\nWith tuples:")

records = [
    ('Alice', 30, 'NYC'),
    ('Bob', 25, 'LA'),
    ('Charlie', 35, 'Chicago')
]

# Sort by age (index 1)
sorted_by_age = sorted(records, key=operator.itemgetter(1))
print("Sorted by age:")
for r in sorted_by_age:
    print(f"  {r}")

# Sort by multiple fields (city, then age)
sorted_multi = sorted(records, key=operator.itemgetter(2, 1))
print("\nSorted by city then age:")
for r in sorted_multi:
    print(f"  {r}")

# Map and filter
print("\nMap and filter:")

users = [
    {'id': 1, 'name': 'Alice', 'active': True},
    {'id': 2, 'name': 'Bob', 'active': False},
    {'id': 3, 'name': 'Charlie', 'active': True}
]

# Extract all names
names = list(map(operator.itemgetter('name'), users))
print(f"Names: {names}")

# Extract ids of active users
active_ids = [
    operator.itemgetter('id')(user)
    for user in users
    if user['active']
]
print(f"Active IDs: {active_ids}")

# Min/max
print("\nMin/max:")

products = [
    {'name': 'Widget', 'price': 29.99},
    {'name': 'Gadget', 'price': 49.99},
    {'name': 'Tool', 'price': 19.99}
]

cheapest = min(products, key=operator.itemgetter('price'))
print(f"Cheapest: {cheapest}")

most_expensive = max(products, key=operator.itemgetter('price'))
print(f"Most expensive: {most_expensive}")

# Grouping
print("\nGrouping:")

from itertools import groupby

transactions = [
    {'category': 'food', 'amount': 50},
    {'category': 'transport', 'amount': 20},
    {'category': 'food', 'amount': 30},
    {'category': 'transport', 'amount': 15}
]

# Must sort first for groupby
transactions.sort(key=operator.itemgetter('category'))

for category, items in groupby(transactions, 
                               key=operator.itemgetter('category')):
    total = sum(operator.itemgetter('amount')(item) for item in items)
    print(f"{category}: ${total}")

"""operator.itemgetter examples"""

import operator

# Basic itemgetter
print("Basic itemgetter:")

# Get single item
get_first = operator.itemgetter(0)
numbers = 

print(f"get_first({numbers}): {get_first(numbers)}")

# Get by key
data = {'name': 'Alice', 'age': 30}
get_name = operator.itemgetter('name')
print(f"get_name({data}): {get_name(data)}")

# Multiple items
print("\nMultiple items:")

# Get multiple indices
get_items = operator.itemgetter(0, 2, 4)
letters = ['a', 'b', 'c', 'd', 'e']

result = get_items(letters)
print(f"get_items({letters}): {result}")

# Multiple keys
get_info = operator.itemgetter('name', 'age')
person = {'name': 'Bob', 'age': 25, 'city': 'NYC'}
print(f"get_info: {get_info(person)}")

# Sorting lists
print("\nSorting lists:")

students = [
    {'name': 'Charlie', 'grade': 85},
    {'name': 'Alice', 'grade': 92},
    {'name': 'Bob', 'grade': 78}
]

# Sort by grade
sorted_by_grade = sorted(students, key=operator.itemgetter('grade'))
print("Sorted by grade:")
for s in sorted_by_grade:
    print(f"  {s}")

# Sort by name
sorted_by_name = sorted(students, key=operator.itemgetter('name'))
print("\nSorted by name:")
for s in sorted_by_name:
    print(f"  {s}")

# Nested sorting
print("\nNested sorting:")

data = [
    {'name': 'Alice', 'scores': [85, 90]},
    {'name': 'Bob', 'scores': [92, 88]},
    {'name': 'Charlie', 'scores': [78, 95]}
]

# Sort by first score
sorted_data = sorted(data, 
    key=lambda x: operator.itemgetter(0)(x['scores']))

print("Sorted by first score:")
for d in sorted_data:
    print(f"  {d}")

# With tuples
print("\nWith tuples:")

records = [
    ('Alice', 30, 'NYC'),
    ('Bob', 25, 'LA'),
    ('Charlie', 35, 'Chicago')
]

# Sort by age (index 1)
sorted_by_age = sorted(records, key=operator.itemgetter(1))
print("Sorted by age:")
for r in sorted_by_age:
    print(f"  {r}")

# Sort by multiple fields (city, then age)
sorted_multi = sorted(records, key=operator.itemgetter(2, 1))
print("\nSorted by city then age:")
for r in sorted_multi:
    print(f"  {r}")

# Map and filter
print("\nMap and filter:")

users = [
    {'id': 1, 'name': 'Alice', 'active': True},
    {'id': 2, 'name': 'Bob', 'active': False},
    {'id': 3, 'name': 'Charlie', 'active': True}
]

# Extract all names
names = list(map(operator.itemgetter('name'), users))
print(f"Names: {names}")

# Extract ids of active users
active_ids = [
    operator.itemgetter('id')(user)
    for user in users
    if user['active']
]
print(f"Active IDs: {active_ids}")

# Min/max
print("\nMin/max:")

products = [
    {'name': 'Widget', 'price': 29.99},
    {'name': 'Gadget', 'price': 49.99},
    {'name': 'Tool', 'price': 19.99}
]

cheapest = min(products, key=operator.itemgetter('price'))
print(f"Cheapest: {cheapest}")

most_expensive = max(products, key=operator.itemgetter('price'))
print(f"Most expensive: {most_expensive}")

# Grouping
print("\nGrouping:")

from itertools import groupby

transactions = [
    {'category': 'food', 'amount': 50},
    {'category': 'transport', 'amount': 20},
    {'category': 'food', 'amount': 30},
    {'category': 'transport', 'amount': 15}
]

# Must sort first for groupby
transactions.sort(key=operator.itemgetter('category'))

for category, items in groupby(transactions, 
                               key=operator.itemgetter('category')):
    total = sum(operator.itemgetter('amount')(item) for item in items)
    print(f"{category}: ${total}")

attrgetter.py
"""operator.attrgetter examples"""

import operator
from datetime import datetime

# Basic attrgetter
print("Basic attrgetter:")

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __repr__(self):
        return f"Person('{self.name}', {self.age})"

# Get single attribute
get_name = operator.attrgetter('name')
person = Person('Alice', 30)

print(f"get_name({person}): {get_name(person)}")
print(f"Equivalent to: {person.name}")

# Multiple attributes
print("\nMultiple attributes:")

# Get multiple attributes
get_info = operator.attrgetter('name', 'age')
result = get_info(person)
print(f"get_info: {result}")

# Sorting objects
print("\nSorting objects:")

people = [
    Person('Charlie', 35),
    Person('Alice', 30),
    Person('Bob', 25)
]

# Sort by name
sorted_by_name = sorted(people, key=operator.attrgetter('name'))
print("Sorted by name:")
for p in sorted_by_name:
    print(f"  {p}")

# Sort by age
sorted_by_age = sorted(people, key=operator.attrgetter('age'))
print("\nSorted by age:")
for p in sorted_by_age:
    print(f"  {p}")

# Nested attributes
print("\nNested attributes:")

class Address:
    def __init__(self, city, zip_code):
        self.city = city
        self.zip = zip_code
    
    def __repr__(self):
        return f"Address('{self.city}', '{self.zip}')"

class Employee:
    def __init__(self, name, address):
        self.name = name
        self.address = address
    
    def __repr__(self):
        return f"Employee('{self.name}', {self.address})"

employees = [
    Employee('Alice', Address('NYC', '10001')),
    Employee('Bob', Address('LA', '90001')),
    Employee('Charlie', Address('Chicago', '60601'))
]

# Access nested attribute
get_city = operator.attrgetter('address.city')
cities = [get_city(emp) for emp in employees]
print(f"Cities: {cities}")

# Sort by nested attribute
sorted_by_city = sorted(employees, key=operator.attrgetter('address.city'))
print("\nSorted by city:")
for emp in sorted_by_city:
    print(f"  {emp}")

# Min/max
print("\nMin/max:")

class Product:
    def __init__(self, name, price, quantity):
        self.name = name
        self.price = price
        self.quantity = quantity
    
    def __repr__(self):
        return f"Product('{self.name}', ${self.price}, qty={self.quantity})"

products = [
    Product('Widget', 29.99, 100),
    Product('Gadget', 49.99, 50),
    Product('Tool', 19.99, 200)
]

cheapest = min(products, key=operator.attrgetter('price'))
print(f"Cheapest: {cheapest}")

most_stock = max(products, key=operator.attrgetter('quantity'))
print(f"Most stock: {most_stock}")

# Map operations
print("\nMap operations:")

class Student:
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

students = [
    Student('Alice', 92),
    Student('Bob', 85),
    Student('Charlie', 78)
]

# Extract all names
names = list(map(operator.attrgetter('name'), students))
print(f"Names: {names}")

# Extract all grades
grades = list(map(operator.attrgetter('grade'), students))
print(f"Grades: {grades}")

# Multiple attributes
info = list(map(operator.attrgetter('name', 'grade'), students))
print(f"Info: {info}")

# Filtering
print("\nFiltering:")

class Task:
    def __init__(self, name, priority, completed):
        self.name = name
        self.priority = priority
        self.completed = completed
    
    def __repr__(self):
        return f"Task('{self.name}', priority={self.priority}, done={self.completed})"

tasks = [
    Task('Review code', 3, False),
    Task('Fix bug', 1, True),
    Task('Write tests', 2, False)
]

# High priority incomplete tasks
high_priority = [
    t for t in tasks
    if operator.attrgetter('priority')(t) <= 2 and not t.completed
]

print("High priority incomplete:")
for t in high_priority:
    print(f"  {t}")

# Grouping
print("\nGrouping:")

from itertools import groupby

class Record:
    def __init__(self, category, value):
        self.category = category
        self.value = value

records = [
    Record('A', 10),
    Record('B', 20),
    Record('A', 15),
    Record('B', 25),
    Record('A', 5)
]

# Must sort first
records.sort(key=operator.attrgetter('category'))

for category, items in groupby(records, 
                               key=operator.attrgetter('category')):
    values = [operator.attrgetter('value')(r) for r in items]
    print(f"{category}: {values}, sum={sum(values)}")

# With datetime
print("\nWith datetime:")

class Event:
    def __init__(self, name, timestamp):
        self.name = name
        self.timestamp = timestamp
    
    def __repr__(self):
        return f"Event('{self.name}', {self.timestamp})"

events = [
    Event('Login', datetime(2024, 1, 15, 10, 30)),
    Event('Purchase', datetime(2024, 1, 15, 11, 45)),
    Event('Logout', datetime(2024, 1, 15, 9, 15))
]

# Sort chronologically
sorted_events = sorted(events, key=operator.attrgetter('timestamp'))
print("Chronological order:")
for e in sorted_events:
    print(f"  {e}")

methodcaller.py
"""operator.methodcaller examples"""

import operator

# Basic methodcaller
print("Basic methodcaller:")

# Call method with no arguments
upper = operator.methodcaller('upper')
text = "hello"

print(f"upper('{text}'): {upper(text)}")
print(f"Equivalent to: {text.upper()}")

# Call with arguments
replace_o = operator.methodcaller('replace', 'o', 'x')
result = replace_o("hello world")
print(f"replace_o('hello world'): {result}")

# String methods
print("\nString methods:")

strings = ['hello', 'WORLD', 'Python']

# Map upper
upper_strings = list(map(operator.methodcaller('upper'), strings))
print(f"Upper: {upper_strings}")

# Map lower
lower_strings = list(map(operator.methodcaller('lower'), strings))
print(f"Lower: {lower_strings}")

# Map strip
padded = ['  hello  ', '  world  ', '  python  ']
stripped = list(map(operator.methodcaller('strip'), padded))
print(f"Stripped: {stripped}")

# With arguments
print("\nWith arguments:")

# Replace method
texts = ['hello world', 'foo bar', 'baz qux']
replaced = list(map(operator.methodcaller('replace', 'o', 'X'), texts))
print(f"Replace 'o' with 'X': {replaced}")

# Split method
split_lines = list(map(operator.methodcaller('split', ','), 
                      ['a,b,c', 'd,e,f', 'g,h,i']))
print(f"Split by comma: {split_lines}")

# Startswith
check_start = operator.methodcaller('startswith', 'hel')
words = ['hello', 'help', 'world', 'helper']
results = [check_start(w) for w in words]
print(f"Startswith 'hel': {list(zip(words, results))}")

# Custom objects
print("\nCustom objects:")

class Counter:
    def __init__(self, value=0):
        self.value = value
    
    def increment(self, amount=1):
        self.value += amount
        return self.value
    
    def reset(self):
        self.value = 0
        return self.value
    
    def __repr__(self):
        return f"Counter({self.value})"

counters = [Counter(10), Counter(20), Counter(30)]

# Call reset on all
reset_all = operator.methodcaller('reset')
for c in counters:
    reset_all(c)

print(f"After reset: {counters}")

# Increment all by 5
increment_5 = operator.methodcaller('increment', 5)
for c in counters:
    increment_5(c)

print(f"After increment(5): {counters}")

# List methods
print("\nList methods:")

lists = [[3, 1, 2], [6, 4, 5], [9, 7, 8]]

# Sort each list
sort_list = operator.methodcaller('sort')
for lst in lists:
    sort_list(lst)

print(f"After sort: {lists}")

# Append to each
append_10 = operator.methodcaller('append', 10)
for lst in lists:
    append_10(lst)

print(f"After append(10): {lists}")

# Dict methods
print("\nDict methods:")

dicts = [
    {'a': 1, 'b': 2},
    {'c': 3, 'd': 4},
    {'e': 5, 'f': 6}
]

# Get keys from all dicts
get_keys = operator.methodcaller('keys')
all_keys = [list(get_keys(d)) for d in dicts]
print(f"All keys: {all_keys}")

# Get values from all dicts
get_values = operator.methodcaller('values')
all_values = [list(get_values(d)) for d in dicts]
print(f"All values: {all_values}")

# Filtering
print("\nFiltering:")

class Task:
    def __init__(self, name, priority):
        self.name = name
        self.priority = priority
    
    def is_high_priority(self):
        return self.priority <= 2
    
    def __repr__(self):
        return f"Task('{self.name}', {self.priority})"

tasks = [
    Task('Fix bug', 1),
    Task('Write docs', 3),
    Task('Review code', 2),
    Task('Update tests', 4)
]

# Filter using method
is_high = operator.methodcaller('is_high_priority')
high_priority = [t for t in tasks if is_high(t)]

print("High priority tasks:")
for t in high_priority:
    print(f"  {t}")

# Chaining operations
print("\nChaining operations:")

class Text:
    def __init__(self, value):
        self.value = value
    
    def upper(self):
        return Text(self.value.upper())
    
    def reverse(self):
        return Text(self.value[::-1])
    
    def strip(self):
        return Text(self.value.strip())
    
    def __repr__(self):
        return f"Text('{self.value}')"

text = Text('  hello  ')

# Apply operations
operations = [
    operator.methodcaller('strip'),
    operator.methodcaller('upper'),
    operator.methodcaller('reverse')
]

result = text
for op in operations:
    result = op(result)

print(f"Original: {text}")
print(f"After operations: {result}")

# With kwargs
print("\nWith kwargs:")

# Split with keyword arguments
split_max = operator.methodcaller('split', ',', maxsplit=2)
result = split_max('a,b,c,d,e')
print(f"Split with maxsplit=2: {result}")

# Format with kwargs
data = {'name': 'Alice', 'age': 30}
format_str = operator.methodcaller('format', **data)
template = "Name: {name}, Age: {age}"
result = format_str(template)
print(f"Formatted: {result}")

arithmetic.py
"""Arithmetic operator functions"""

import operator
from functools import reduce

# Basic arithmetic
print("Basic arithmetic:")

# Addition
print(f"add(5, 3): {operator.add(5, 3)}")
print(f"5 + 3: {5 + 3}")

# Subtraction
print(f"sub(10, 4): {operator.sub(10, 4)}")

# Multiplication
print(f"mul(6, 7): {operator.mul(6, 7)}")

# Division
print(f"truediv(15, 4): {operator.truediv(15, 4)}")
print(f"floordiv(15, 4): {operator.floordiv(15, 4)}")

# Modulo
print(f"mod(17, 5): {operator.mod(17, 5)}")

# Power
print(f"pow(2, 8): {operator.pow(2, 8)}")

# With reduce
print("\nWith reduce:")

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

# Sum using add
total = reduce(operator.add, numbers)
print(f"Sum: {total}")

# Product using mul
product = reduce(operator.mul, numbers)
print(f"Product: {product}")

# Factorial
n = 5
factorial = reduce(operator.mul, range(1, n + 1))
print(f"{n}! = {factorial}")

# Unary operations
print("\nUnary operations:")

# Negation
print(f"neg(5): {operator.neg(5)}")
print(f"neg(-3): {operator.neg(-3)}")

# Positive
print(f"pos(5): {operator.pos(5)}")
print(f"pos(-5): {operator.pos(-5)}")

# Absolute value
print(f"abs(-10): {operator.abs(-10)}")
print(f"abs(7): {operator.abs(7)}")

# String operations
print("\nString operations:")

# String concatenation
result = operator.add("Hello", " World")
print(f"add('Hello', ' World'): {result}")

# String repetition
result = operator.mul("Hi", 3)
print(f"mul('Hi', 3): {result}")

# Join strings with reduce
words = ['Python', 'is', 'awesome']
sentence = reduce(lambda a, b: operator.add(operator.add(a, ' '), b), words)
print(f"Joined: {sentence}")

# List operations
print("\nList operations:")

# List concatenation
list1 = [1, 2, 3]
list2 = [4, 5, 6]
combined = operator.add(list1, list2)
print(f"add([1,2,3], [4,5,6]): {combined}")

# List repetition
repeated = operator.mul([1, 2], 3)
print(f"mul([1,2], 3): {repeated}")

# Concatenate multiple lists
lists = [[1, 2], [3, 4], [5, 6]]
flattened = reduce(operator.add, lists)
print(f"Flattened: {flattened}")

# Map operations
print("\nMap operations:")

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

# Double all numbers
doubled = list(map(lambda x: operator.mul(x, 2), numbers))
print(f"Doubled: {doubled}")

# Square all numbers
squared = list(map(lambda x: operator.pow(x, 2), numbers))
print(f"Squared: {squared}")

# Negate all numbers
negated = list(map(operator.neg, numbers))
print(f"Negated: {negated}")

# Calculator functions
print("\nCalculator functions:")

def calculate(a, b, op):
    """Apply operator to two numbers"""
    return op(a, b)

print(f"calculate(10, 5, add): {calculate(10, 5, operator.add)}")
print(f"calculate(10, 5, sub): {calculate(10, 5, operator.sub)}")
print(f"calculate(10, 5, mul): {calculate(10, 5, operator.mul)}")
print(f"calculate(10, 5, truediv): {calculate(10, 5, operator.truediv)}")

# Operation dispatch
print("\nOperation dispatch:")

operations = {
    '+': operator.add,
    '-': operator.sub,
    '*': operator.mul,
    '/': operator.truediv,
    '%': operator.mod,
    '**': operator.pow
}

def eval_expr(a, op, b):
    """Evaluate simple expression"""
    return operations[op](a, b)

print(f"10 + 5 = {eval_expr(10, '+', 5)}")
print(f"10 - 5 = {eval_expr(10, '-', 5)}")
print(f"10 * 5 = {eval_expr(10, '*', 5)}")
print(f"10 / 5 = {eval_expr(10, '/', 5)}")
print(f"10 % 3 = {eval_expr(10, '%', 3)}")
print(f"2 ** 8 = {eval_expr(2, '**', 8)}")

# Cumulative operations
print("\nCumulative operations:")

values = [10, -5, 3, -2, 8]

# Cumulative sum
running_sum = []
total = 0
for v in values:
    total = operator.add(total, v)
    running_sum.append(total)
print(f"Values: {values}")
print(f"Running sum: {running_sum}")

# Cumulative product
running_product = []
product = 1
for v in values:
    product = operator.mul(product, v)
    running_product.append(product)
print(f"Running product: {running_product}")

# In-place operations
print("\nIn-place operations:")

# iadd (+=)
a = 10
a = operator.iadd(a, 5)
print(f"After iadd(10, 5): {a}")

# imul (*=)
b = 3
b = operator.imul(b, 4)
print(f"After imul(3, 4): {b}")

# List iadd
lst = [1, 2, 3]
lst = operator.iadd(lst, [4, 5])
print(f"List after iadd: {lst}")

comparison.py
"""Comparison operator functions"""

import operator

# Basic comparisons
print("Basic comparisons:")

# Equality
print(f"eq(5, 5): {operator.eq(5, 5)}")
print(f"eq(5, 3): {operator.eq(5, 3)}")

# Not equal
print(f"ne(5, 3): {operator.ne(5, 3)}")
print(f"ne(5, 5): {operator.ne(5, 5)}")

# Less than
print(f"lt(3, 5): {operator.lt(3, 5)}")
print(f"lt(5, 3): {operator.lt(5, 3)}")

# Less than or equal
print(f"le(5, 5): {operator.le(5, 5)}")
print(f"le(3, 5): {operator.le(3, 5)}")

# Greater than
print(f"gt(5, 3): {operator.gt(5, 3)}")
print(f"gt(3, 5): {operator.gt(3, 5)}")

# Greater than or equal
print(f"ge(5, 5): {operator.ge(5, 5)}")
print(f"ge(5, 3): {operator.ge(5, 3)}")

# Filtering
print("\nFiltering:")

numbers = [1, 5, 10, 15, 20, 25]

# Filter >= 10
greater_eq_10 = [x for x in numbers if operator.ge(x, 10)]
print(f"Numbers >= 10: {greater_eq_10}")

# Filter < 15
less_than_15 = [x for x in numbers if operator.lt(x, 15)]
print(f"Numbers < 15: {less_than_15}")

# Filter == 10
equals_10 = list(filter(lambda x: operator.eq(x, 10), numbers))
print(f"Numbers == 10: {equals_10}")

# String comparisons
print("\nString comparisons:")

words = ['apple', 'banana', 'cherry', 'date']

# Equal to 'banana'
is_banana = list(filter(lambda w: operator.eq(w, 'banana'), words))
print(f"Equal to 'banana': {is_banana}")

# Less than 'cherry'
before_cherry = [w for w in words if operator.lt(w, 'cherry')]
print(f"Before 'cherry': {before_cherry}")

# Custom objects
print("\nCustom objects:")

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __repr__(self):
        return f"Person('{self.name}', {self.age})"

people = [
    Person('Alice', 30),
    Person('Bob', 25),
    Person('Charlie', 35)
]

# Find people over 25
over_25 = [p for p in people if operator.gt(p.age, 25)]
print("Over 25:")
for p in over_25:
    print(f"  {p}")

# Find exactly age 30
age_30 = [p for p in people if operator.eq(p.age, 30)]
print(f"\nAge 30: {age_30}")

# Identity and membership
print("\nIdentity and membership:")

a = [1, 2, 3]
b = [1, 2, 3]
c = a

# Identity (is)
print(f"is_(a, b): {operator.is_(a, b)}")
print(f"is_(a, c): {operator.is_(a, c)}")

# Not identity (is not)
print(f"is_not(a, b): {operator.is_not(a, b)}")
print(f"is_not(a, c): {operator.is_not(a, c)}")

# Containment (in)
print(f"contains([1,2,3], 2): {operator.contains([1, 2, 3], 2)}")
print(f"contains([1,2,3], 5): {operator.contains([1, 2, 3], 5)}")
print(f"contains('hello', 'ell'): {operator.contains('hello', 'ell')}")

# Truth testing
print("\nTruth testing:")

# truth() - test if true
print(f"truth(True): {operator.truth(True)}")
print(f"truth(False): {operator.truth(False)}")
print(f"truth([]): {operator.truth([])}")
print(f"truth([1,2,3]): {operator.truth([1, 2, 3])}")
print(f"truth(0): {operator.truth(0)}")
print(f"truth(42): {operator.truth(42)}")

# not_() - logical not
print(f"not_(True): {operator.not_(True)}")
print(f"not_(False): {operator.not_(False)}")
print(f"not_([]): {operator.not_([])}")
print(f"not_([1,2,3]): {operator.not_([1, 2, 3])}")

# Sorting with comparisons
print("\nSorting with comparisons:")

data = [
    {'name': 'Alice', 'score': 85},
    {'name': 'Bob', 'score': 92},
    {'name': 'Charlie', 'score': 78}
]

# Sort by score descending
from functools import cmp_to_key

def compare_score(a, b):
    """Compare by score (descending)"""
    if operator.gt(a['score'], b['score']):
        return -1  # a comes first
    elif operator.lt(a['score'], b['score']):
        return 1   # b comes first
    return 0

sorted_data = sorted(data, key=cmp_to_key(compare_score))
print("Sorted by score (desc):")
for d in sorted_data:
    print(f"  {d}")

# Validation
print("\nValidation:")

def validate_age(age):
    """Validate age is between 0 and 150"""
    return operator.ge(age, 0) and operator.le(age, 150)

ages = [25, -5, 30, 200, 45]
for age in ages:
    valid = validate_age(age)
    print(f"Age {age}: {'valid' if valid else 'invalid'}")

# Range checking
print("\nRange checking:")

def in_range(value, min_val, max_val):
    """Check if value is in range [min, max]"""
    return operator.ge(value, min_val) and operator.le(value, max_val)

print(f"in_range(5, 1, 10): {in_range(5, 1, 10)}")
print(f"in_range(15, 1, 10): {in_range(15, 1, 10)}")
print(f"in_range(1, 1, 10): {in_range(1, 1, 10)}")

# Count occurrences
print("\nCount occurrences:")

values = [1, 2, 3, 2, 4, 2, 5]
target = 2

count = sum(1 for v in values if operator.eq(v, target))
print(f"Count of {target}: {count}")

# Count in range
count_range = sum(1 for v in values if operator.ge(v, 2) and operator.le(v, 4))
print(f"Count in [2, 4]: {count_range}")

operator functions Named functions that perform the same operations as Python operators (+, -, [], .) but can be passed as arguments to other functions.
itemgetter Creates a callable that retrieves items by key or index - cleaner and faster than lambda x: x['key'] for sorting and mapping.
attrgetter Creates a callable that retrieves object attributes - supports nested attributes like 'address.city' and multiple attributes.
methodcaller Creates a callable that invokes a named method with optional arguments - useful for applying the same method across many objects.

Exercise: practical.py

Sort a list of records by multiple fields using itemgetter and attrgetter