Python Fundamentals

The building blocks. Master these cold before moving on.

Variables & Types

Basic Types

# Strings
hostname = "ise-01.inside.domusdigitalis.dev"
ip = "10.50.1.20"

# Numbers
port = 443
timeout = 30.5
vlan_id = 10

# Booleans
is_active = True
has_certificate = False

# None
result = None

Type Checking

# Check type
type(hostname)      # <class 'str'>
type(port)          # <class 'int'>
type(timeout)       # <class 'float'>

# isinstance (preferred)
isinstance(port, int)           # True
isinstance(port, (int, float))  # True - multiple types

Type Conversion

# String to int
port = int("443")

# Int to string
port_str = str(443)

# String to float
timeout = float("30.5")

# To boolean
bool("")        # False
bool("text")    # True
bool(0)         # False
bool(1)         # True
bool([])        # False
bool([1,2])     # True

Strings

String Operations

hostname = "ise-01.inside.domusdigitalis.dev"

# Length
len(hostname)  # 32

# Case
hostname.upper()    # "ISE-01.INSIDE.DOMUSDIGITALIS.DEV"
hostname.lower()    # "ise-01.inside.domusdigitalis.dev"
hostname.title()    # "Ise-01.Inside.Domusdigitalis.Dev"

# Check content
hostname.startswith("ise")    # True
hostname.endswith(".dev")     # True
"inside" in hostname          # True

# Split and join
parts = hostname.split(".")   # ["ise-01", "inside", "domusdigitalis", "dev"]
".".join(parts)               # Back to original

# Strip whitespace
"  spaced  ".strip()    # "spaced"
"  spaced  ".lstrip()   # "spaced  "
"  spaced  ".rstrip()   # "  spaced"

# Replace
hostname.replace("ise-01", "ise-02")

F-Strings (Formatted Strings)

hostname = "ise-01"
ip = "10.50.1.20"
port = 443

# Basic interpolation
url = f"https://{hostname}:{port}/api"

# Expressions inside
msg = f"Port {port} is {'open' if port == 443 else 'closed'}"

# Formatting numbers
pi = 3.14159
f"{pi:.2f}"         # "3.14" - 2 decimal places
f"{1000000:,}"      # "1,000,000" - thousands separator
f"{port:05d}"       # "00443" - zero-padded

# Width and alignment
f"{'left':<10}"     # "left      "
f"{'right':>10}"    # "     right"
f"{'center':^10}"   # "  center  "

Raw Strings & Multiline

# Raw string (no escape processing) - good for regex
pattern = r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"

# Multiline
config = """
interface GigabitEthernet0/1
  description Uplink to Core
  switchport mode trunk
"""

# Multiline with f-string
vlan = 10
interface = f"""
interface Vlan{vlan}
  ip address 10.50.{vlan}.1 255.255.255.0
  no shutdown
"""

Operators

Arithmetic

a, b = 10, 3

a + b   # 13 - addition
a - b   # 7  - subtraction
a * b   # 30 - multiplication
a / b   # 3.333... - division (float)
a // b  # 3  - floor division (int)
a % b   # 1  - modulo (remainder)
a ** b  # 1000 - power

# Augmented assignment
count = 0
count += 1   # count = count + 1
count -= 1   # count = count - 1
count *= 2   # count = count * 2

Comparison

a, b = 10, 20

a == b   # False - equal
a != b   # True  - not equal
a < b    # True  - less than
a <= b   # True  - less than or equal
a > b    # False - greater than
a >= b   # False - greater than or equal

# Chained comparisons
x = 15
10 < x < 20   # True - x is between 10 and 20

# Identity (same object, not just equal value)
a = [1, 2, 3]
b = [1, 2, 3]
c = a

a == b    # True  - same value
a is b    # False - different objects
a is c    # True  - same object

Logical

# and - both must be True
True and True    # True
True and False   # False

# or - at least one True
True or False    # True
False or False   # False

# not - invert
not True    # False
not False   # True

# Practical example
is_admin = True
is_active = True
has_permission = False

# Access granted if admin AND active, OR has explicit permission
can_access = (is_admin and is_active) or has_permission

Control Flow

if/elif/else

status_code = 200

if status_code == 200:
    print("Success")
elif status_code == 404:
    print("Not Found")
elif status_code >= 500:
    print("Server Error")
else:
    print(f"Unknown status: {status_code}")

# Ternary (inline if)
result = "Success" if status_code == 200 else "Error"

# Multiple conditions
if status_code == 200 and response_time < 1.0:
    print("Fast success")

match (Python 3.10+)

status_code = 404

match status_code:
    case 200:
        print("OK")
    case 301 | 302:
        print("Redirect")
    case 400:
        print("Bad Request")
    case 404:
        print("Not Found")
    case _ if status_code >= 500:
        print("Server Error")
    case _:
        print("Unknown")

# Pattern matching with structure
response = {"status": "error", "code": 500}

match response:
    case {"status": "ok", "data": data}:
        print(f"Got data: {data}")
    case {"status": "error", "code": code}:
        print(f"Error {code}")

for Loops

# Iterate over list
hosts = ["ise-01", "ise-02", "ise-03"]
for host in hosts:
    print(host)

# With index
for i, host in enumerate(hosts):
    print(f"{i}: {host}")

# Iterate over dict
config = {"hostname": "ise-01", "ip": "10.50.1.20", "port": 443}

for key in config:
    print(key)

for key, value in config.items():
    print(f"{key} = {value}")

# Range
for i in range(5):        # 0, 1, 2, 3, 4
    print(i)

for i in range(1, 6):     # 1, 2, 3, 4, 5
    print(i)

for i in range(0, 10, 2): # 0, 2, 4, 6, 8 (step by 2)
    print(i)

while Loops

# Basic while
count = 0
while count < 5:
    print(count)
    count += 1

# With break
while True:
    response = check_status()
    if response == "ready":
        break
    time.sleep(1)

# With continue
for i in range(10):
    if i % 2 == 0:  # Skip even numbers
        continue
    print(i)  # Only prints odd: 1, 3, 5, 7, 9

# While with else (runs if no break)
attempts = 0
while attempts < 3:
    if try_connect():
        print("Connected!")
        break
    attempts += 1
else:
    print("Failed after 3 attempts")

Exception Handling

try/except

# Basic
try:
    result = 10 / 0
except ZeroDivisionError:
    print("Cannot divide by zero")

# Multiple exceptions
try:
    value = int(user_input)
except ValueError:
    print("Invalid number")
except TypeError:
    print("Wrong type")

# Catch multiple in one
try:
    risky_operation()
except (ValueError, TypeError) as e:
    print(f"Error: {e}")

# Catch all (use sparingly)
try:
    risky_operation()
except Exception as e:
    print(f"Unexpected error: {e}")

try/except/else/finally

try:
    file = open("config.json")
    data = json.load(file)
except FileNotFoundError:
    print("Config not found")
    data = {}
except json.JSONDecodeError:
    print("Invalid JSON")
    data = {}
else:
    # Runs only if no exception
    print(f"Loaded {len(data)} items")
finally:
    # Always runs (cleanup)
    if 'file' in locals():
        file.close()

# Better: context manager
try:
    with open("config.json") as f:
        data = json.load(f)
except (FileNotFoundError, json.JSONDecodeError) as e:
    print(f"Error loading config: {e}")
    data = {}

Raising Exceptions

def validate_ip(ip: str) -> bool:
    parts = ip.split(".")
    if len(parts) != 4:
        raise ValueError(f"Invalid IP format: {ip}")

    for part in parts:
        if not part.isdigit() or not 0 <= int(part) <= 255:
            raise ValueError(f"Invalid octet: {part}")

    return True

# Re-raise with context
try:
    validate_ip(user_input)
except ValueError as e:
    raise ValueError(f"Configuration error: {e}") from e

Practice Exercises

Exercise 1: Parse ISE Hostname

# Given: "ise-psn-01.inside.domusdigitalis.dev"
# Extract: role (psn), number (01), domain (inside.domusdigitalis.dev)

hostname = "ise-psn-01.inside.domusdigitalis.dev"

# Solution
parts = hostname.split(".")
node_parts = parts[0].split("-")

role = node_parts[1]           # "psn"
number = node_parts[2]         # "01"
domain = ".".join(parts[1:])   # "inside.domusdigitalis.dev"

print(f"Role: {role}, Number: {number}, Domain: {domain}")

Exercise 2: Validate VLAN Range

def is_valid_vlan(vlan_id: int) -> bool:
    """Check if VLAN ID is valid (1-4094, excluding reserved)."""
    if not isinstance(vlan_id, int):
        return False
    if vlan_id < 1 or vlan_id > 4094:
        return False
    if vlan_id in (1, 1002, 1003, 1004, 1005):  # Reserved
        return False
    return True

# Test
print(is_valid_vlan(10))     # True
print(is_valid_vlan(1))      # False (reserved)
print(is_valid_vlan(5000))   # False (out of range)

Next Module

Data Structures - Lists, dicts, sets, and comprehensions.