Python Essentials for AI Engineers · Lesson 11 of 36
Defining and Calling Functions
Defining and Calling Functions
Python
# Basic function definition
def greet(name: str) -> str:
return f"Hello, {name}"
# Call
result = greet("Pharmacist")
print(result) # "Hello, Pharmacist"
# Function with no return value (implicitly returns None)
def log_event(event: str) -> None:
print(f"[LOG] {event}")
log_event("Agent invoked") # Prints log line, returns NoneParameters and Arguments
Python
# Positional arguments — order matters
def calculate_dose(drug: str, weight_kg: float, dose_per_kg: float) -> float:
return weight_kg * dose_per_kg
# Positional call
result = calculate_dose("vancomycin", 70.0, 15.0) # 1050.0
# Keyword arguments — order doesn't matter
result = calculate_dose(dose_per_kg=15.0, drug="vancomycin", weight_kg=70.0)
# Mixing: positional first, then keyword
result = calculate_dose("vancomycin", weight_kg=70.0, dose_per_kg=15.0)Default Values
Python
def calculate_adjusted_dose(
drug: str,
weight_kg: float,
dose_per_kg: float = 15.0, # Default: 15 mg/kg
renal_impairment: bool = False, # Default: no impairment
) -> float:
dose = weight_kg * dose_per_kg
if renal_impairment:
dose *= 0.75 # 25% reduction
return round(dose, 1)
# Use all defaults
print(calculate_adjusted_dose("vancomycin", 70.0)) # 1050.0
# Override one default
print(calculate_adjusted_dose("vancomycin", 70.0, renal_impairment=True)) # 787.5
# Override all
print(calculate_adjusted_dose("vancomycin", 70.0, 20.0, True)) # 1050.0Multiple Return Values
Python functions can return multiple values as a tuple:
Python
def evaluate_inr(inr: float) -> tuple[str, str]:
"""Return status and recommended action for a given INR."""
if inr < 2.0:
return "subtherapeutic", "Increase warfarin dose; re-check INR in 1 week"
elif inr <= 3.0:
return "therapeutic", "Continue current dose; routine monitoring"
elif inr <= 4.5:
return "supratherapeutic", "Hold 1 dose; re-check INR in 2-3 days"
else:
return "critically_elevated", "Hold warfarin; consider vitamin K; urgent review"
# Unpack the tuple
status, action = evaluate_inr(4.2)
print(f"Status: {status}")
print(f"Action: {action}")
# Or keep as tuple
result = evaluate_inr(2.4)
print(result) # ("therapeutic", "Continue current dose...")Docstrings
Docstrings document what a function does — used by IDEs, help(), and AI code assistants:
Python
def check_drug_interaction(drug_a: str, drug_b: str) -> dict:
"""
Check for clinically significant interactions between two drugs.
Args:
drug_a: First drug (generic name preferred)
drug_b: Second drug (generic name preferred)
Returns:
Dict with keys: severity (str), mechanism (str), recommendation (str)
Returns empty dict if no interaction found.
Raises:
ValueError: If either drug name is empty
"""
if not drug_a or not drug_b:
raise ValueError("Drug names cannot be empty")
...Access docstrings:
Python
help(check_drug_interaction)
print(check_drug_interaction.__doc__)Functions as First-Class Objects
In Python, functions are objects — they can be assigned, passed, and returned:
Python
# Assign a function to a variable
formatter = str.upper
print(formatter("warfarin")) # "WARFARIN"
# Pass a function as an argument
def apply_to_drugs(drugs: list[str], transform) -> list[str]:
return [transform(drug) for drug in drugs]
drugs = ["warfarin", "aspirin", "metformin"]
print(apply_to_drugs(drugs, str.upper)) # ["WARFARIN", "ASPIRIN", "METFORMIN"]
print(apply_to_drugs(drugs, str.title)) # ["Warfarin", "Aspirin", "Metformin"]
# Return a function from a function (closure)
def make_dose_calculator(base_dose_mg_per_kg: float):
"""Return a function that calculates doses for a fixed base dose."""
def calculate(weight_kg: float) -> float:
return base_dose_mg_per_kg * weight_kg
return calculate
vancomycin_dose = make_dose_calculator(15.0)
gentamicin_dose = make_dose_calculator(5.0)
print(vancomycin_dose(70.0)) # 1050.0
print(gentamicin_dose(70.0)) # 350.0Functions in AI/ML Code
Python
# Pattern 1: Processing pipelines — each step is a function
def load_text(filepath: str) -> str:
with open(filepath) as f:
return f.read()
def clean_text(text: str) -> str:
return text.lower().strip()
def chunk_text(text: str, size: int = 500) -> list[str]:
return [text[i:i+size] for i in range(0, len(text), size)]
# Pipeline — compose functions
def process_document(filepath: str) -> list[str]:
raw = load_text(filepath)
cleaned = clean_text(raw)
return chunk_text(cleaned)
# Pattern 2: Strategy pattern — pass behavior as a function
def evaluate_answers(
questions: list[str],
answers: list[str],
judge_fn, # Any callable that takes (question, answer) and returns float
) -> list[float]:
return [judge_fn(q, a) for q, a in zip(questions, answers)]
def simple_length_judge(question: str, answer: str) -> float:
return min(1.0, len(answer) / 200) # Crude proxy for quality
scores = evaluate_answers(questions, answers, simple_length_judge)
# Pattern 3: Map/filter/reduce with functions
drug_names = ["warfarin", "aspirin", "metformin", "lisinopril"]
lengths = list(map(len, drug_names)) # [8, 7, 9, 10]
long_drugs = list(filter(lambda d: len(d) > 7, drug_names)) # ["warfarin", "metformin", "lisinopril"]Variable Scope
Python
# Global scope
model_name = "gpt-4o"
def generate_answer(question: str) -> str:
# Local scope — model_name from global is accessible (read-only)
print(f"Using model: {model_name}") # Works
return "..."
def update_model(new_name: str) -> None:
global model_name # Required to assign to global variable
model_name = new_name
# Avoid global state — pass values explicitly instead
def generate_answer_clean(question: str, model: str = "gpt-4o") -> str:
print(f"Using model: {model}")
return "..."Pure Functions vs Side Effects
Pure functions: same input always gives same output, no external state modified.
Python
# Pure function — easy to test, cache, parallelize
def normalize_score(score: float, min_val: float, max_val: float) -> float:
return (score - min_val) / (max_val - min_val)
# Impure function — has side effects (I/O, mutation, external state)
def log_and_normalize(score: float, min_val: float, max_val: float) -> float:
print(f"Normalizing {score}") # Side effect: I/O
return (score - min_val) / (max_val - min_val)
# In AI code: prefer pure functions for transformations
# Keep side effects (DB writes, API calls, logging) at the edges of your system