Master Python function arguments: default values, keyword arguments, variable-length arguments.
# Default arguments
def greet(name, msg="Hello"):
print(f"{msg}, {name}!")
greet("Alice")
greet("Bob", "Hi")
# Keyword arguments
def add(a, b):
return a + b
print(add(2, 3))
print(add(b=5, a=7))
# *args and **kwargs
def demo(*args, **kwargs):
print("args:", args)
print("kwargs:", kwargs)
demo(1, 2, 3, x=10, y=20)
# Argument unpacking
nums = (4, 5)
print(add(*nums))
params = {'a': 8, 'b': 9}
print(add(**params))
# Function with all types of arguments
def all_args(a, b=2, *args, c=3, **kwargs):
print(f"a={a}, b={b}, args={args}, c={c}, kwargs={kwargs}")
all_args(1)
all_args(1, 10, 20, 30, c=99, x=5, y=6)
# Mutable default argument pitfall
def append_to_list(val, lst=[]):
lst.append(val)
return lst
print(append_to_list(1))
print(append_to_list(2)) # Same list reused!
# Correct way
def append_to_list_safe(val, lst=None):
if lst is None:
lst = []
lst.append(val)
return lst
print(append_to_list_safe(1))
print(append_to_list_safe(2))
Use anonymous functions and functional programming tools in Python.
# Lambda
square = lambda x: x**2
print(square(5))
# map
nums = [1, 2, 3, 4]
squares = list(map(lambda x: x**2, nums))
print(squares)
# filter
evens = list(filter(lambda x: x%2==0, nums))
print(evens)
# reduce
from functools import reduce
product = reduce(lambda x, y: x*y, nums)
print(product)
# Named vs lambda
def cube(x):
return x**3
print(list(map(cube, nums)))
# filter with condition
greater_than_two = list(filter(lambda x: x > 2, nums))
print(greater_than_two)
# reduce for sum
total = reduce(lambda x, y: x + y, nums)
print(total)
# Sorting with lambda
words = ['apple', 'banana', 'pear', 'grape']
words_sorted = sorted(words, key=lambda w: len(w))
print(words_sorted)
Understand iterators, the yield keyword, and generator expressions.
# Iterator
lst = [10, 20, 30]
itr = iter(lst)
print(next(itr))
print(next(itr))
print(next(itr))
# Custom iterator
class CountDown:
def __init__(self, start):
self.n = start
def __iter__(self):
return self
def __next__(self):
if self.n <= 0:
raise StopIteration
val = self.n
self.n -= 1
return val
for x in CountDown(3):
print(x)
# Generator function
def gen():
yield 1
yield 2
yield 3
for x in gen():
print(x)
# Generator with state
def countdown(n):
while n > 0:
yield n
n -= 1
for i in countdown(5):
print(i)
# Infinite generator
def infinite():
n = 0
while True:
yield n
n += 1
# Use with caution: next(infinite())
# Generator expression
genexp = (x**2 for x in range(4))
print(list(genexp))
# Using send() with generator
def echo():
while True:
val = (yield)
print(f"Received: {val}")
e = echo()
next(e)
e.send('Hello')
Learn to use and write function decorators in Python, including functools.wraps.
# Simple decorator
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Before call")
result = func(*args, **kwargs)
print("After call")
return result
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
# Decorator with functools.wraps
from functools import wraps
def log(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
return func(*args, **kwargs)
return wrapper
@log
def add(a, b):
return a + b
print(add(2, 3))
print(add.__name__)
# Decorator with arguments
def repeat(n):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for _ in range(n):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(3)
def greet():
print("Hi!")
greet()
# Class as decorator
class Uppercase:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
result = self.func(*args, **kwargs)
return str(result).upper()
@Uppercase
def say(text):
return text
print(say("hello world"))
Use the with statement and create custom context managers with __enter__ and __exit__.
# Using with statement
with open("test.txt", "w") as f:
f.write("Hello, file!\n")
# Reading file safely
with open("test.txt") as f:
for line in f:
print(line.strip())
# Custom context manager (class)
class MyContext:
def __enter__(self):
print("Entering context")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("Exiting context")
if exc_type:
print(f"Exception: {exc_type}")
return False # Propagate exception
with MyContext():
print("Inside context")
# Custom context manager (function)
from contextlib import contextmanager
@contextmanager
def my_cm():
print("Enter")
yield
print("Exit")
with my_cm():
print("Doing stuff")
# Suppressing exceptions
from contextlib import suppress
with suppress(ZeroDivisionError):
print(1/0)
# Nested context managers
from contextlib import ExitStack
with ExitStack() as stack:
f1 = stack.enter_context(open("test.txt"))
# Add more context managers as needed