Drill 04: Functions & Classes
Functions with args/kwargs, lambda, classes, and modern dataclasses.
Run This Drill
bash ~/atelier/_bibliotheca/domus-captures/docs/modules/ROOT/examples/python-drills/04-functions.sh
Drill Script
#!/bin/bash
# PYTHON DRILL 04: FUNCTIONS & CLASSES
# Paste this entire script into your terminal
# Topics: def, args, kwargs, classes, dataclasses
echo "=================================================================="
echo " PYTHON DRILL 04: FUNCTIONS & CLASSES "
echo "=================================================================="
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "DRILL 4.1: BASIC FUNCTIONS"
echo "Definition, arguments, return values"
echo "------------------------------------------------------------------"
echo ""
python3 << 'PYEOF'
# Simple function
def greet(name):
return f"Hello, {name}!"
print(greet("admin"))
# Multiple arguments
def connect(host, port, timeout=30):
return f"Connecting to {host}:{port} (timeout: {timeout}s)"
print(connect("10.50.1.20", 443))
print(connect("10.50.1.20", 443, timeout=60))
# Return multiple values
def parse_endpoint(endpoint):
host, port = endpoint.split(":")
return host, int(port)
h, p = parse_endpoint("10.50.1.20:443")
print(f"Host: {h}, Port: {p}")
PYEOF
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "DRILL 4.2: *args AND **kwargs"
echo "Variable arguments"
echo "------------------------------------------------------------------"
echo ""
python3 << 'PYEOF'
# *args - variable positional arguments
def sum_all(*numbers):
return sum(numbers)
print(f"sum_all(1,2,3): {sum_all(1, 2, 3)}")
print(f"sum_all(10,20,30,40): {sum_all(10, 20, 30, 40)}")
# **kwargs - variable keyword arguments
def create_server(**config):
for key, value in config.items():
print(f" {key}: {value}")
print("create_server(hostname='ise-01', ip='10.50.1.20'):")
create_server(hostname="ise-01", ip="10.50.1.20", port=443)
# Combining both
def api_call(endpoint, *args, **kwargs):
print(f"Endpoint: {endpoint}")
print(f"Args: {args}")
print(f"Kwargs: {kwargs}")
print("\napi_call('/users', 1, 2, format='json'):")
api_call("/users", 1, 2, format="json", timeout=30)
PYEOF
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "DRILL 4.3: LAMBDA FUNCTIONS"
echo "Anonymous functions for quick operations"
echo "------------------------------------------------------------------"
echo ""
python3 << 'PYEOF'
# Basic lambda
square = lambda x: x ** 2
print(f"square(5): {square(5)}")
# Sorting with lambda
servers = [
{"name": "ise-01", "cpu": 45},
{"name": "bind-01", "cpu": 5},
{"name": "vault-01", "cpu": 78}
]
# Sort by CPU
by_cpu = sorted(servers, key=lambda s: s["cpu"])
print("\nSorted by CPU:")
for s in by_cpu:
print(f" {s['name']}: {s['cpu']}%")
# Sort descending
by_cpu_desc = sorted(servers, key=lambda s: s["cpu"], reverse=True)
print("\nSorted by CPU (desc):")
for s in by_cpu_desc:
print(f" {s['name']}: {s['cpu']}%")
# Filter with lambda
high_cpu = list(filter(lambda s: s["cpu"] > 20, servers))
print(f"\nHigh CPU (>20%): {[s['name'] for s in high_cpu]}")
PYEOF
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "DRILL 4.4: BASIC CLASSES"
echo "Object-oriented programming"
echo "------------------------------------------------------------------"
echo ""
python3 << 'PYEOF'
class Server:
def __init__(self, hostname, ip, port=22):
self.hostname = hostname
self.ip = ip
self.port = port
self.status = "unknown"
def connect(self):
return f"Connecting to {self.hostname} ({self.ip}:{self.port})"
def set_status(self, status):
self.status = status
def __str__(self):
return f"Server({self.hostname}, {self.ip}, {self.status})"
# Create instances
ise = Server("ise-01", "10.50.1.20", 443)
vault = Server("vault-01", "10.50.1.132", 8200)
print(ise.connect())
ise.set_status("active")
print(ise)
print(f"\nvault.hostname: {vault.hostname}")
print(f"vault.ip: {vault.ip}")
PYEOF
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "DRILL 4.5: DATACLASSES"
echo "Modern Python for data containers"
echo "------------------------------------------------------------------"
echo ""
python3 << 'PYEOF'
from dataclasses import dataclass, field
from typing import List
@dataclass
class Endpoint:
hostname: str
ip: str
port: int = 22
tags: List[str] = field(default_factory=list)
# Auto-generated __init__, __repr__, __eq__
ise = Endpoint("ise-01", "10.50.1.20", 443, ["production", "pan"])
print(f"Created: {ise}")
print(f"Hostname: {ise.hostname}")
print(f"Tags: {ise.tags}")
# Equality comparison works
ise2 = Endpoint("ise-01", "10.50.1.20", 443, ["production", "pan"])
print(f"\nise == ise2: {ise == ise2}")
# Create list of endpoints
endpoints = [
Endpoint("ise-01", "10.50.1.20", 443),
Endpoint("bind-01", "10.50.1.90", 53),
Endpoint("vault-01", "10.50.1.132", 8200)
]
print("\n=== All endpoints ===")
for ep in endpoints:
print(f" {ep.hostname}: {ep.ip}:{ep.port}")
PYEOF
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "DRILL 4.6: INHERITANCE"
echo "Extending classes"
echo "------------------------------------------------------------------"
echo ""
python3 << 'PYEOF'
class Device:
def __init__(self, hostname, ip):
self.hostname = hostname
self.ip = ip
def ping(self):
return f"Pinging {self.ip}..."
class NetworkDevice(Device):
def __init__(self, hostname, ip, vendor):
super().__init__(hostname, ip)
self.vendor = vendor
def show_version(self):
return f"{self.vendor} device at {self.ip}"
class ISENode(NetworkDevice):
def __init__(self, hostname, ip, roles):
super().__init__(hostname, ip, "Cisco")
self.roles = roles
def get_roles(self):
return f"{self.hostname} roles: {', '.join(self.roles)}"
# Use inheritance chain
ise = ISENode("ise-01", "10.50.1.20", ["pan", "mnt", "psn"])
print(ise.ping()) # From Device
print(ise.show_version()) # From NetworkDevice
print(ise.get_roles()) # From ISENode
PYEOF
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "YOUR TURN - TRY THESE:"
echo "------------------------------------------------------------------"
echo ""
echo "1. Create a function with default args:"
echo " def fetch_data(url, timeout=30, retries=3):"
echo " return f'Fetching {url} (timeout={timeout}, retries={retries})'"
echo ""
echo "2. Sort list of dicts:"
echo " data = [{'name': 'z'}, {'name': 'a'}, {'name': 'm'}]"
echo " sorted(data, key=lambda x: x['name'])"
echo ""
echo "3. Create a dataclass:"
echo " from dataclasses import dataclass"
echo " @dataclass"
echo " class VLAN:"
echo " id: int"
echo " name: str"
echo " subnet: str = ''"
echo ""
echo "------------------------------------------------------------------"
echo "KEY TAKEAWAYS:"
echo "1. def name(args, default=value): for functions"
echo "2. *args for variable positional, **kwargs for keyword"
echo "3. lambda x: expression for quick functions"
echo "4. class Name: with __init__ for classes"
echo "5. @dataclass for data containers"
echo "------------------------------------------------------------------"