Python Fundamentals
Master the core concepts of Python essential for DevOps automation and scripting.
Data Types
Numbers
Python supports two main numeric types:
Integers - whole numbers without decimals
count = 42
port = 8080
Floats - numbers with decimals
cpu_usage = 95.5
disk_percent = 78.25
Common operations:
# Arithmetic operators
result = 10 + 5 # Addition
result = 10 - 5 # Subtraction
result = 10 * 5 # Multiplication
result = 10 / 5 # Division (returns float)
result = 10 // 3 # Floor division (returns 3)
result = 10 % 3 # Modulo/remainder (returns 1)
result = 2 ** 8 # Exponentiation (returns 256)
Strings
Text data enclosed in quotes:
message = "Hello, DevOps!"
filename = 'config.yaml'
# Multi-line strings
description = """
This is a multi-line
string for documentation
"""
String formatting:
# f-strings (Python 3.6+) - recommended
name = "server"
ip = "192.168.1.10"
print(f"Connecting to {name} at {ip}")
# .format() method
print("Server: {}, Status: {}".format("prod-01", "healthy"))
# % formatting (legacy)
print("Error code: %d" % 500)
String operations:
text = "DevOps Pipeline"
print(len(text)) # Length: 15
print(text.lower()) # devops pipeline
print(text.upper()) # DEVOPS PIPELINE
print(text.replace("Pipeline", "Workflow"))
print("DevOps" in text) # True
Boolean
True or False values:
is_running = True
is_healthy = False
container_status = True
Collections
Lists - ordered, mutable collections
servers = ["web-01", "web-02", "db-01"]
ports = [8080, 8443, 5432]
# Access elements
print(servers[0]) # web-01
print(servers[-1]) # db-01 (last element)
# Slicing
print(servers[1:]) # ["web-02", "db-01"]
print(servers[:2]) # ["web-01", "web-02"]
# Modify lists
servers.append("cache-01")
servers.remove("db-01")
servers.pop(0) # Remove and return first element
Tuples - ordered, immutable collections
config = ("localhost", 3306, "mysql")
print(config[1]) # 3306
# Good for function return values
address, port, service = config
Dictionaries - key-value pairs
server_info = {
"hostname": "prod-web-01",
"ip": "10.0.0.5",
"port": 8080,
"status": "running"
}
# Access values
print(server_info["hostname"]) # prod-web-01
print(server_info.get("port", 80)) # 8080 (default to 80 if missing)
# Modify dictionaries
server_info["status"] = "stopped"
server_info["uptime"] = 48 # Add new key
# Iterate
for key, value in server_info.items():
print(f"{key}: {value}")
Sets - unordered, unique collections
active_services = {"nginx", "postgresql", "redis"}
running = {"nginx", "redis", "docker"}
# Set operations
print(active_services & running) # Intersection: {"nginx", "redis"}
print(active_services | running) # Union
print(active_services - running) # Difference
Control Flow
Conditionals
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("Other status")
Comparison operators:
x == y # Equal
x != y # Not equal
x > y # Greater than
x < y # Less than
x >= y # Greater than or equal
x <= y # Less than or equal
Logical operators:
if status == "running" and cpu < 80:
print("Service healthy")
if env == "prod" or env == "staging":
print("Production environment")
if not is_error:
print("No errors detected")
Loops
For loops - iterate over sequences
servers = ["web-01", "web-02", "db-01"]
for server in servers:
print(f"Checking {server}")
# With index
for index, server in enumerate(servers):
print(f"{index}: {server}")
# Range
for i in range(5): # 0 to 4
print(i)
for i in range(2, 10, 2): # 2, 4, 6, 8
print(i)
While loops - repeat while condition is true
retry_count = 0
max_retries = 3
while retry_count < max_retries:
try:
response = connect_to_server()
break
except ConnectionError:
retry_count += 1
print(f"Retry {retry_count} of {max_retries}")
Loop control:
for service in services:
if service == "temp":
continue # Skip this iteration
if service == "broken":
break # Exit loop
process(service)
Functions
Reusable blocks of code:
def greet(name):
"""Print a greeting message."""
print(f"Hello, {name}!")
greet("DevOps")
Parameters and return values:
def check_port(host, port, timeout=5):
"""Check if a port is open."""
# Default parameter: timeout=5
try:
socket.create_connection((host, port), timeout)
return True
except socket.timeout:
return False
# Call with positional arguments
result = check_port("localhost", 8080)
# Call with keyword arguments
result = check_port(host="localhost", port=8080, timeout=10)
Multiple return values:
def parse_log_line(line):
"""Extract timestamp and message from log line."""
timestamp, message = line.split(" - ")
return timestamp, message
ts, msg = parse_log_line("2024-01-15 10:30:00 - Service started")
Variable-length arguments:
def print_servers(*servers):
"""Print all provided servers."""
for server in servers:
print(server)
print_servers("web-01", "web-02", "db-01")
def create_config(**kwargs):
"""Create config from keyword arguments."""
config = {}
for key, value in kwargs.items():
config[key] = value
return config
cfg = create_config(host="localhost", port=3306, user="admin")
File I/O
Reading files:
# Read entire file
with open("config.yaml", "r") as f:
content = f.read()
# Read line by line
with open("access.log", "r") as f:
for line in f:
print(line.strip())
# Read all lines as list
with open("hosts.txt", "r") as f:
lines = f.readlines()
Writing files:
# Write new file (overwrites if exists)
with open("output.txt", "w") as f:
f.write("Line 1\n")
f.write("Line 2\n")
# Append to file
with open("output.txt", "a") as f:
f.write("Line 3\n")
# Write multiple lines
with open("servers.txt", "w") as f:
f.writelines(["server1\n", "server2\n", "server3\n"])
Best practice - use context managers:
# Context manager (with statement) automatically closes file
with open("data.txt", "r") as f:
data = f.read()
# File is automatically closed here
Error Handling
Try-except blocks:
try:
file = open("missing.txt", "r")
content = file.read()
except FileNotFoundError:
print("File not found")
except IOError as e:
print(f"Error reading file: {e}")
finally:
# Always runs
if file:
file.close()
Common exceptions:
# Value error
int("not a number") # ValueError
# Index error
list_var = [1, 2, 3]
print(list_var[10]) # IndexError
# Key error
dict_var = {"a": 1}
print(dict_var["z"]) # KeyError
# Type error
result = "string" + 5 # TypeError
# Attribute error
class Config:
pass
cfg = Config()
print(cfg.missing_attr) # AttributeError
Raising exceptions:
def validate_port(port):
if not isinstance(port, int) or port < 0 or port > 65535:
raise ValueError(f"Invalid port: {port}")
return port
try:
validate_port(99999)
except ValueError as e:
print(f"Validation failed: {e}")
Modules and Imports
Standard library modules:
import os # Operating system
import sys # System functions
import subprocess # Run external commands
import json # JSON parsing
import yaml # YAML parsing
import logging # Logging
import shutil # File operations
# Use imported module
print(os.getcwd())
result = subprocess.run(["ls", "-la"], capture_output=True)
Importing specific items:
from datetime import datetime, timedelta
from pathlib import Path
now = datetime.now()
tomorrow = now + timedelta(days=1)
config_path = Path("/etc/app/config.yaml")
Creating your own modules:
# utils.py
def validate_hostname(hostname):
return len(hostname) > 0 and "." in hostname
def parse_config(filepath):
with open(filepath, "r") as f:
return f.read()
# main.py
import utils
if utils.validate_hostname("prod.example.com"):
print("Valid")
Object-Oriented Programming Basics
Classes:
class Server:
"""Represents a server."""
def __init__(self, hostname, ip, port=22):
"""Initialize server."""
self.hostname = hostname
self.ip = ip
self.port = port
self.status = "unknown"
def connect(self):
"""Connect to server."""
print(f"Connecting to {self.hostname} ({self.ip}:{self.port})")
self.status = "connected"
def disconnect(self):
"""Disconnect from server."""
self.status = "disconnected"
def info(self):
"""Return server info."""
return f"{self.hostname} at {self.ip}:{self.port} ({self.status})"
# Create and use objects
server = Server("web-01", "10.0.0.5", 22)
server.connect()
print(server.info())
Inheritance:
class LinuxServer(Server):
"""Linux-specific server."""
def __init__(self, hostname, ip, port=22, distro="Ubuntu"):
super().__init__(hostname, ip, port)
self.distro = distro
def update_packages(self):
print(f"Updating packages on {self.hostname}")
class WindowsServer(Server):
"""Windows-specific server."""
def __init__(self, hostname, ip, port=3389):
super().__init__(hostname, ip, port)
def update_windows(self):
print(f"Running Windows Update on {self.hostname}")
Exercises
-
Temperature Converter: Write a function that converts Celsius to Fahrenheit. Test it with multiple values.
-
Log Parser: Read a log file, count errors by type, and return a dictionary with counts.
-
Configuration Validator: Create a class that validates server configurations (hostname, IP, port).
-
File Backup: Write a script that backs up all
.conffiles from/etc/to a backup directory, preserving directory structure. -
List Processing: Given a list of numbers, write a function that returns those divisible by 3, using list comprehension.
-
Exception Handling: Improve the temperature converter to handle invalid input gracefully.
-
Module Creation: Create a
network_utils.pymodule with functions for validating IP addresses and checking host connectivity.
Summary
- Data types: int, float, string, list, tuple, dict, set, bool
- Control flow: if/elif/else, for, while loops
- Functions: reusable code with parameters and return values
- File I/O: reading and writing with context managers
- Error handling: try-except-finally for robust code
- Modules: reuse code across files
- OOP: classes for organized, reusable code structures