Backend Systemsbeginner
Python Functions, File I/O, and Error Handling
Master Python functions (args, kwargs, type hints, decorators), file I/O (reading, writing, CSV, JSON), and error handling with try/except.
Asma HafeezApril 17, 20266 min read
pythonfunctionsfile-ioerror-handling
Python Functions, File I/O, and Error Handling
Functions
Defining Functions
Python
def greet(name: str) -> str:
"""Return a greeting message."""
return f"Hello, {name}!"
result = greet("Asma") # "Hello, Asma!"Default Arguments
Python
def create_user(name: str, role: str = "viewer", active: bool = True) -> dict:
return {"name": name, "role": role, "active": active}
create_user("Asma") # {"name":"Asma","role":"viewer","active":True}
create_user("Ali", role="admin") # override default
create_user("Bob", "editor", False) # positional*args and **kwargs
Python
# *args — variable positional arguments (tuple)
def sum_all(*numbers: int) -> int:
return sum(numbers)
sum_all(1, 2, 3, 4, 5) # 15
# **kwargs — variable keyword arguments (dict)
def log_event(event: str, **metadata):
print(f"[{event}]", metadata)
log_event("user.login", user_id=42, ip="192.168.1.1")
# [user.login] {'user_id': 42, 'ip': '192.168.1.1'}
# Combining
def mixed(required, *args, key="default", **kwargs):
print(required, args, key, kwargs)
mixed("hello", 1, 2, 3, key="custom", extra="data")
# hello (1, 2, 3) custom {'extra': 'data'}Type Hints
Python
from typing import Optional, Union, Callable
def find_user(user_id: int) -> Optional[dict]:
# Returns a dict or None
...
def process(value: Union[int, str]) -> str:
return str(value)
def apply(fn: Callable[[int], int], n: int) -> int:
return fn(n)
# Python 3.10+ — cleaner union syntax
def find(id: int) -> dict | None: ...Lambda Functions
Python
# Short anonymous functions
double = lambda x: x * 2
add = lambda x, y: x + y
# Most useful in sort keys and higher-order functions
products = [{"name": "B", "price": 50}, {"name": "A", "price": 30}]
products.sort(key=lambda p: p["price"])
products.sort(key=lambda p: (-p["price"], p["name"])) # price desc, name asc
# filter and map with lambda
numbers = [1, 2, 3, 4, 5, 6]
evens = list(filter(lambda x: x % 2 == 0, numbers))
doubled = list(map(lambda x: x * 2, numbers))Decorators
Python
import time
from functools import wraps
# A decorator is a function that wraps another function
def timer(func):
@wraps(func) # preserves the original function's metadata
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} took {elapsed:.3f}s")
return result
return wrapper
@timer
def slow_function():
time.sleep(0.1)
return "done"
slow_function() # prints: slow_function took 0.101s
# Decorator with arguments
def retry(max_attempts: int = 3, exceptions=(Exception,)):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for attempt in range(1, max_attempts + 1):
try:
return func(*args, **kwargs)
except exceptions as e:
if attempt == max_attempts:
raise
print(f"Attempt {attempt} failed: {e}. Retrying...")
return wrapper
return decorator
@retry(max_attempts=3, exceptions=(ConnectionError,))
def fetch_data(url: str) -> dict:
...File I/O
Reading Files
Python
# Read entire file
with open("data.txt", "r", encoding="utf-8") as f:
content = f.read()
# Read line by line (memory efficient for large files)
with open("data.txt", "r") as f:
for line in f:
print(line.strip()) # strip removes trailing newline
# Read all lines into list
with open("data.txt", "r") as f:
lines = f.readlines() # includes newlines
lines = [l.strip() for l in f] # clean versionWriting Files
Python
# Write (overwrites existing file)
with open("output.txt", "w", encoding="utf-8") as f:
f.write("Hello, World!\n")
f.write("Second line\n")
# Append (adds to existing file)
with open("log.txt", "a") as f:
f.write(f"[{timestamp}] User logged in\n")
# Write multiple lines
lines = ["line 1", "line 2", "line 3"]
with open("output.txt", "w") as f:
f.writelines(f"{line}\n" for line in lines)CSV Files
Python
import csv
# Read CSV
with open("users.csv", "r", newline="", encoding="utf-8") as f:
reader = csv.DictReader(f) # uses first row as field names
for row in reader:
print(row["name"], row["email"])
# Write CSV
users = [
{"name": "Asma", "email": "asma@example.com", "role": "admin"},
{"name": "Ali", "email": "ali@example.com", "role": "user"},
]
with open("users.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["name", "email", "role"])
writer.writeheader()
writer.writerows(users)JSON Files
Python
import json
# Read JSON
with open("config.json", "r") as f:
config = json.load(f) # dict/list
# Write JSON
data = {"name": "Asma", "scores": [95, 87, 92]}
with open("data.json", "w") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
# JSON string <-> Python object
json_str = json.dumps(data) # Python → JSON string
python_obj = json.loads(json_str) # JSON string → Python
# Custom serialization
from datetime import datetime
def json_default(obj):
if isinstance(obj, datetime):
return obj.isoformat()
raise TypeError(f"Not serializable: {type(obj)}")
json.dumps({"created": datetime.now()}, default=json_default)Error Handling
try / except / else / finally
Python
def divide(a: float, b: float) -> float:
try:
result = a / b
except ZeroDivisionError:
print("Cannot divide by zero")
return 0.0
except TypeError as e:
print(f"Wrong types: {e}")
raise # re-raise the exception
else:
# Only runs if no exception was raised
print(f"Result: {result}")
return result
finally:
# Always runs — use for cleanup
print("Division attempted")Catching Multiple Exceptions
Python
try:
value = int(input("Enter a number: "))
result = 100 / value
except (ValueError, ZeroDivisionError) as e:
print(f"Error: {e}")Custom Exceptions
Python
class AppError(Exception):
"""Base exception for the application."""
pass
class ValidationError(AppError):
def __init__(self, field: str, message: str):
self.field = field
self.message = message
super().__init__(f"{field}: {message}")
class NotFoundError(AppError):
def __init__(self, resource: str, id: int):
super().__init__(f"{resource} with id {id} not found")
# Usage
def get_user(user_id: int) -> dict:
user = db.find(user_id)
if user is None:
raise NotFoundError("User", user_id)
return user
def validate_age(age: int):
if age < 0 or age > 150:
raise ValidationError("age", f"must be between 0 and 150, got {age}")
# Catching specific hierarchy
try:
validate_age(-5)
except ValidationError as e:
print(f"Validation failed on '{e.field}': {e.message}")
except AppError as e:
print(f"Application error: {e}")Context Managers
The with statement ensures cleanup happens even if an exception occurs.
Python
# File (built-in context manager)
with open("file.txt") as f:
data = f.read()
# f.close() is called automatically
# Custom context manager
from contextlib import contextmanager
@contextmanager
def timer():
start = time.perf_counter()
try:
yield # code in the `with` block runs here
finally:
elapsed = time.perf_counter() - start
print(f"Elapsed: {elapsed:.3f}s")
with timer():
time.sleep(0.5)
# Elapsed: 0.500sKey Takeaways
- Use
*argsand**kwargsto write flexible, reusable functions - Type hints make code self-documenting and enable IDE autocompletion
- Always use
with open(...)for files — it guarantees the file is closed csv.DictReaderandjson.load** for structured file reading — don't parse manually- Custom exceptions make error handling specific and debuggable
- Decorators let you add cross-cutting concerns (logging, retry, caching) without modifying the original function
Enjoyed this article?
Explore the Backend Systems learning path for more.
Found this helpful?
Leave a comment
Have a question, correction, or just found this helpful? Leave a note below.