Lambda Functions/shaare/PlNZLA
Lambda Functions
- Python functions defined with
defallow multiple statements, clear naming, and support for docstrings, making them ideal for complex or reusable logic. - In many cases, you need a simple, single-expression function to pass directly to another function without the ceremony of a full definition.
- The
lambdakeyword lets you create small, anonymous functions inline, avoiding the verbosity of adeffor trivial operations. - These one-line functions are particularly handy when you want to supply custom behavior to built-in higher-order functions without polluting your namespace with one-off function names.
Syntax of a lambda Function
- A
lambdafunction follows the exact pattern:lambda <A rguments>: <expression>, where the expression result is implicitly returned. - The
lambdakeyword introduces the function,<arguments>lists its parameters, and a colon separates them from the single expression body. - You cannot include multiple statements, loops, or traditional
if/elseblocks: only a single expression or a ternary expression. - Compared to a
deffunction, alambdais nameless and concise, making it ideal for inline usage where defining a named function would be overkill.
square = lambda x: x * x
print(square(5))
print((lambda a, b: a + b)(3, 4))
Custom Sorting with sorted()
- The built-in
sorted()function accepts an optionalkeyparameter, which should be a function that returns a comparison key for each element. - Using a
lambdafor thekeyargument lets you define the sorting logic inline without a separate function definition. - This approach keeps your code concise and focused, especially when the key logic is a simple attribute extraction or computation.
- When you need more complex sorting logic, you can still fall back to a named
deffunction for clarity.
services = [("web-app", 3), ("database", 1), ("cache", 5), ("api-gateway", 2)]
print(f"Default sort: {sorted(services)}")
def get_replica_count(svc_tuple):
return svc_tuple[1]
print(f"Sorting by replica count - standard function: {sorted(services, key=get_replica_count)}")
print(f"Sorting by replica count - lambda function: {sorted(services, key=lambda svc: svc[1])}")
Transforming Data with map()
- The
map(function, iterable)built-in applies a given function to each item in an iterable, producing an iterator of results. - Using a
lambdawithmaplets you specify simple transformations inline without an extra function definition. - Although list comprehensions are often preferred for readability,
mapwith alambdacan be concise when you already need an iterator or want to emphasize the function-application nature. - Remember that
mapreturns a lazy iterator; convert it to a list if you need to access all results at once.
my_numbers = [1, 2, 3, 4]
print(list(map(lambda num: num * 2, my_numbers)))
ports = [80, 443, 8080, 22]
port_descriptions = list(map(lambda port: f"Port {port} is open", ports))
print(port_descriptions)
Filtering Data with filter()
- The
filter(function, iterable)built-in yields only those items for which the function returnsTrue. - A
lambdainfilteris perfect for inline tests, such as checking attributes or simple conditions. - As with
map,filterreturns an iterator, so wrap it inlist()to evaluate immediately if needed. - While list comprehensions can express filtering more idiomatically in modern Python,
filterremains a clear demonstration of higher-order function usage.
ports = [80, 443, 8080, 22, 5432]
privileged_ports = list(filter(lambda port: port < 1024, ports))
print(privileged_ports)
privileged_comprehension = [port for port in ports if port < 1024]
print(privileged_comprehension)
<DT>
<A HREF="/shaare/qwGUkw" ADD_DATE="1773933396" LAST_MODIFIED="1773933396" PRIVATE="0" TAGS="python">*args and *kwargs</A><DD># Flexible Functions: `argsand**kwargs`
- We can use the syntax
*argsand**kwargsto accept a variable number of both positional and keyword arguments.
def example_function(*args, **kwargs):
print(f"Positional args: {args}")
print(f"Keyword args: {kwargs}")
example_function(1, 2, 3, a="Value", b=True)
*args in Definition: Collecting Positionals
- Uses
*argsto gather extra positional parameters into a tuple - Allows functions to accept any number of positional inputs
- Common in utilities like custom logging or aggregation functions
def apply_operator(operator, *operands):
"""Applies operator to a variable number of operands. Supports 'add' and 'mul'.
Args:
operator (str): The operator to apply. Must be either 'add' or 'mul'.
*operands (int or float): Zero or more numbers to be combined.
Returns:
int or float: The result of applying the operator on the operands.
Raises:
ValueError: Raised when operator is not 'add' nor 'mul'.
"""
if operator == 'add':
result = sum(operands)
elif operator == 'mul':
result = 1
for n in operands:
result *= n
else:
raise ValueError(f"Unknown operator {operator}. Supported values are 'add' and 'mul'")
return result
print(apply_operator('add', 1, 2, 3, 4))
print(apply_operator('add', 1, 2, 3, 4, 5, 6, 7))
print(apply_operator('add', 1, 2))
print(apply_operator('mul', 1, 2, 3, 4))
print(apply_operator('mul', 1, 2, 3, 4, 5, 6, 7))
print(apply_operator('mul', 1, 2))
# print(apply_operator('div', 1, 2)) # Uncommenting raises ValueError since div is not supported
**kwargs in Definition: Collecting Keywords
- Uses
**kwargsto gather extra named parameters into a dictionary - Ideal for optional configuration flags or settings
- Enables functions to accept flexible keyword arguments without predefining them
def set_options(**settings):
print(f"Received dictionary: {settings}")
for key, value in settings.items():
print(f"\t{key} = {value}")
set_options(timeout=30, user="admin", retries=5)
Order in Definition Matters
- Standard positional parameters must come first, some might also have a default value
- Followed by
*argsto catch extra positionals - Then keyword-only parameters, some might also have a default value
- Finally
**kwargsto catch extra keyword arguments
def process_request(url, method="GET", *headers, timeout, **params):
print(f"url={url}, method={method}, timeout={timeout}")
print(f"headers={headers}")
print(f"params={params}")
process_request("https://www.example.com", timeout=30)
process_request("https://www.example.com", "PUT", timeout=30)
# Equivalent to call above
process_request("https://www.example.com", timeout=30, method="PUT")
process_request(
"https://www.example.com",
"PUT",
"Auth: xyz",
"Content-Type: application/json",
timeout=30
)
process_request(
"https://www.example.com",
"PUT",
"Auth: xyz",
"Content-Type: application/json",
timeout=30,
retries=5,
log_level="DEBUG"
)
* in Call: Unpacking Positional Arguments
- Uses
*sequenceto expand a list or tuple into positional arguments - Sequence length must match the function’s positional parameters
- Useful for dynamic argument lists built at runtime
def connect(host, port, timeout):
print(f"Connecting to {host}:{port} with timeout {timeout}s.")
params = ["db.internal", 5432, 10]
params_with_extra_values = ["db.internal", 5432, 10, "a", True]
connect(*params)
connect(*params_with_extra_values[:3])
** in Call: Unpacking Keyword Arguments
- Uses
**dictto expand key-value pairs into keyword arguments - Dictionary keys must match the function’s parameter names
- Common in configuration-driven function calls
def configure_service(name, version, replicas=1):
print(f"Setting up {name} v{version} with {replicas} replicas...")
config = {"name": "auth-service", "version": "2.1.0", "replicas": 3}
configure_service(**config)
(97)