Note *args collects extra positional arguments as a tuple, **kwargs collects extra keyword arguments as a dict. They can coexist in the same signature.
GET https://api.example.com (timeout=10, retries=3)
Note / marks preceding params as positional-only. * marks following params as keyword-only. This prevents callers from relying on internal parameter names.
keyword onlypositional onlyslash in functionenforce keyword
Lambda (Anonymous Functions)
syntax
lambda params: expression
example
square = lambda n: n ** 2print(square(5))
users = [{"name": "Carol", "age": 28}, {"name": "Alice", "age": 35}]
users.sort(key=lambda u: u["age"])
print([u["name"] for u in users])
output
25
['Carol', 'Alice']
Note Lambdas are limited to a single expression. For anything with statements or multiple lines, use a regular def. Assigning a lambda to a variable is less readable than def.
lambdaanonymous functioninline functionsort key function
Decorators
syntax
@decorator
deffunc(): ...
example
import functools
import time
def timer(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} took {elapsed:.4f}s")
return result
return wrapper
@timer
def compute(n):
return sum(range(n))
compute(1_000_000)
output
compute took 0.0XXXs
Note Always use @functools.wraps(func) in your wrapper so that the decorated function preserves its name and docstring. Decorators run at import time.
Note The inner function captures the enclosing variable by reference, not by value. See Common Mistakes for the late-binding closure gotcha.
closurefunction factorycaptured variableinner function
Generators with yield
syntax
def gen():
yield value
example
def countdown(n: int):
while n > 0:
yield n
n -= 1for tick in countdown(3):
print(tick)
# Generators are lazy — values produced one at a time
nums = countdown(1_000_000)
print(next(nums))
output
3
2
1
1000000
Note Generators produce values lazily, consuming almost no memory regardless of size. They are single-use: once exhausted, they cannot be restarted.
generatoryieldlazy evaluationnext()produce values
Type Hints for Functions
syntax
deffunc(param: Type) -> ReturnType:
example
from collections.abc import Callable
def apply(
values: list[int],
transform: Callable[[int], int]
) -> list[int]:
return [transform(v) for v invalues]
result = apply([1, 2, 3], lambda x: x * 10)
print(result)
output
[10, 20, 30]
Note Use collections.abc.Callable for function type hints. The format is Callable[[ArgTypes], ReturnType]. For complex signatures, consider typing.Protocol.
function type hintcallable typereturn typeparameter types
Recursion
syntax
deffunc(n):
if base_case: return value
returnfunc(modified_n)
example
def factorial(n: int) -> int:
if n <= 1:
return1return n * factorial(n - 1)
print(factorial(6))
import sys
print(f"Recursion limit: {sys.getrecursionlimit()}")
output
720
Recursion limit: 1000
Note Python's default recursion limit is 1000. Deeply recursive algorithms should be rewritten iteratively or use sys.setrecursionlimit() with caution.