14.1 Python Fundamentals for Network Engineers: Data Types, Control Flow, and Libraries
Key Takeaways
Python serves as the core programming environment in network automation, replacing brittle screen-scraping CLI scripts with programmatic, structured API and model-driven interactions.
Primitive data types include strings (str, supporting slicing and f-string interpolation), integers (int), floating-point numbers (float), and booleans (bool) for conditional flags.
Collection data types structure device information: ordered mutable lists (list), immutable tuples (tuple), key-value dictionaries (dict) for configuration trees and API payloads, and unique sets (set) for membership and difference checks.
Control flow mechanisms—including if/elif/else conditionals, for loops over device inventories, polling while loops, and list comprehensions—orchestrate dynamic workflow execution.
The requests library manages HTTP REST API transactions (GET, POST, headers, basic/token auth, JSON parsing), while ncclient establishes SSH-based NETCONF sessions to exchange XML RPCs such as get-config and edit-config.
Python Fundamentals for Network Engineers: Data Types, Control Flow, and Libraries
Enterprise network engineering has transitioned from manual command-line interface (CLI) interactions to programmatic infrastructure automation. Managing hundreds of routers, switches, and wireless access points individually through terminal emulators is slow, prone to human error, and incompatible with modern continuous integration and continuous deployment (CI/CD) pipelines. Python has emerged as the primary programming language for network automation due to its clear readability, extensive standard library, and broad ecosystem of specialized networking packages. Network engineers leverage Python to interact with RESTful APIs on software-defined controllers, dispatch model-driven Remote Procedure Calls (RPCs) over NETCONF, parse structured telemetry, and validate configuration state across complex campus and WAN environments.
+-------------------------------------------------------------------------+
| Network Automation Execution Stack |
+-------------------------------------------------------------------------+
| Application Layer: Custom Python Scripts, Workflows, Audit Logic |
+-------------------------------------------------------------------------+
|
+----------------------------+----------------------------+
v v
+--------------------------------+ +--------------------------------+
| REST Automation Stack | | NETCONF Automation Stack |
| Library: 'requests' | | Library: 'ncclient' |
| Payload: JSON Data / RESTCONF | | Payload: YANG-modeled XML |
| Transport: HTTPS (TCP 443) | | Transport: SSH Subsystem (830)|
+--------------------------------+ +--------------------------------+
| |
+----------------------------+----------------------------+
v
+-------------------------------------------------------------------------+
| Enterprise Infrastructure: Catalyst Switches, Routers, SD-WAN, ISE |
+-------------------------------------------------------------------------+
Primitive Data Types in Network Automation
Python provides several built-in primitive data types that represent fundamental network values such as IP addresses, port identifiers, operational statuses, and interface metrics:
Strings (str)
Strings are immutable sequences of Unicode characters used extensively to store hostnames, interface names, IP addresses, configuration snippets, and raw CLI outputs. Because strings are immutable, operations that manipulate them produce new string objects rather than modifying the original in place.
- Slicing: Python strings support slicing syntax
string[start:stop:step]to extract specific substrings. For example, given an interface identifierintf = "GigabitEthernet0/1", slicingintf[0:2]yields"Gi"for shorthand naming, whileintf[-3:]yields"0/1". - String Methods: Methods such as
.split(),.strip(), and.replace()help parse text. Calling"192.168.10.1/24".split("/")separates an IP prefix into the host address"192.168.10.1"and prefix length"24". - Formatted String Literals (f-strings): Introduced in Python 3.6, f-strings provide an efficient, readable syntax for embedding variables and expressions directly inside string literals by prefixing the string with
forFand placing expressions inside curly braces{}:
hostname = "core-rtr-01"
vlan_id = 100
vlan_name = "USERS"
config_cmd = f"vlan {vlan_id}\n name {vlan_name}\n! Configured on {hostname}"
Integers (int) and Floating-Point Numbers (float)
Integers represent whole numbers of arbitrary precision and are used for Autonomous System (AS) numbers, VLAN IDs (1–4094), Access Control List (ACL) numbers, interface bandwidth values, and TCP/UDP port numbers. Floats represent decimal values, commonly encountered when monitoring interface utilization percentages (e.g., 87.4%), link error rates, or CPU load averages.
Booleans (bool)
Booleans evaluate to either True or False. In network programming, booleans serve as flags indicating operational conditions—such as whether an interface is administratively enabled (is_up = True), whether an authentication token has expired, or whether an API response was successful.
Collection Data Types for Network State
Automating network tasks requires grouping individual primitives into structured collections. Python offers four primary collection types, each with distinct mutability, ordering, and indexing characteristics:
Collection Data Types Overview:
[ List: ["Gi0/1", "Gi0/2"] ] --> Ordered, mutable sequence; indexed by integer
[ Tuple: ("10.1.1.1", 22) ] --> Ordered, immutable sequence; protected record
[ Dict: {"vlan": 10, ...} ] --> Key-value mapping; models structured device state
[ Set: {"10.1.1.1", ...} ] --> Unordered unique values; membership & diffs
Lists (list)
Lists are ordered, mutable sequences enclosed in square brackets []. Elements are zero-indexed and can be modified, appended, or removed dynamically:
- Elements are accessed via their index:
devices[0]retrieves the first element, whiledevices[-1]retrieves the last. - Appending elements:
devices.append("10.1.1.5")adds an address to the end. - Removing elements:
devices.pop()removes and returns the trailing item, while.remove(value)deletes the first occurrence of a specific value. - Common use case: Maintaining an ordered sequence of management IP addresses to iterate across during backup jobs.
Tuples (tuple)
Tuples are ordered, immutable sequences defined using parentheses (). Once created, items cannot be added, replaced, or removed. Tuples provide data integrity for fixed constants that should never change during script execution, such as socket address pairs (ip_address, port) or coordinate pairs.
Dictionaries (dict)
Dictionaries are mutable mappings composed of key-value pairs wrapped in curly braces {}. Keys must be unique and immutable (typically strings), while values can be any Python object, including other dictionaries and lists. Dictionaries represent the core data structure for modeling network configurations, device attributes, and JSON-based API responses:
- Accessing values: Bracket notation
device["ip"]retrieves the value but raises aKeyErrorif the key is missing. Using the safer.get(key, default)method returnsNone(or a specified fallback) if the key does not exist. - Iteration: The
.keys()method returns all keys,.values()returns all values, and.items()returns key-value tuples for simultaneous iteration. - Nested Dictionaries: Deep structures mirror network hierarchies, such as a device containing an
interfacesdictionary where each interface key maps to properties like IP, subnet mask, and administrative state.
Sets (set)
Sets are unordered collections of unique elements enclosed in curly braces {} (or initialized with set()). Sets automatically deduplicate input values and support mathematical set operations:
- Union (
|): Combines elements from both sets. - Intersection (
&): Identifies elements present in both sets. - Difference (
-): Identifies elements present in the first set but absent in the second. - Common use case: Comparing configured VLANs across two distribution switches to detect VLAN trunk mismatches.
Python Data Types Comparison Matrix
| Data Type | Mutability | Syntax / Delimiter | Key Characteristics | Network Automation Use Case |
|---|---|---|---|---|
String (str) | Immutable | Quotes: '...' or "..." | Sequence of Unicode characters; slicing and f-strings | CLI commands, banner generation, IP string storage |
Integer (int) | Immutable | Numeric literal: 42 | Arbitrary precision whole numbers | VLAN IDs, BGP ASNs, TCP/UDP ports, MTU settings |
Float (float) | Immutable | Decimal literal: 3.14 | Double-precision floating-point numbers | Interface load %, telemetry metrics, latency values |
Boolean (bool) | Immutable | Literals: True or False | Logical truth values; subclass of integer | Operational flags, interface state, error checks |
List (list) | Mutable | Square brackets: [...] | Ordered sequence; permits duplicates; zero-indexed | Device inventory lists, interface queues, IP pools |
Tuple (tuple) | Immutable | Parentheses: (...) | Ordered sequence; cannot be modified once set | Socket endpoints (host, port), fixed database records |
Dictionary (dict) | Mutable | Curly braces: {key: val} | Associative key-value mapping; keys must be unique | Parsed JSON payloads, device configs, YANG mappings |
Set (set) | Mutable | Curly braces: {val1, val2} | Unordered collection of unique items; set algebra | VLAN mismatch detection, deduping discovered endpoints |
Control Flow and Iteration Patterns
Control flow statements govern script execution based on runtime conditions, network state, and API response values.
Conditional Logic (if, elif, else)
Conditional statements evaluate boolean expressions using comparison operators (==, !=, <, >, <=, >=) and logical operators (and, or, not, in):
interface_status = "down"
admin_enabled = True
if interface_status == "up":
print("Interface operational")
elif interface_status == "down" and admin_enabled:
print("Alert: Interface administratively up but operationally down!")
else:
print("Interface administratively shut down")
Iteration with for and while Loops
for loops iterate over iterable objects such as lists, dictionaries, or IP networks generated by the ipaddress module:
devices = [
{"hostname": "dist-sw01", "ip": "10.1.1.1", "platform": "iosxe"},
{"hostname": "dist-sw02", "ip": "10.1.1.2", "platform": "iosxe"}
]
for dev in devices:
print(f"Connecting to {dev['hostname']} at {dev['ip']}...")
while loops repeat a block of code as long as a condition evaluates to True. They are frequently used when polling asynchronous jobs on controllers—such as checking whether a configuration push task has finished—typically bounded by a retry counter or timeout to prevent infinite execution.
List Comprehensions
List comprehensions provide a concise, readable syntax for generating new lists from existing iterables by applying expressions and optional filters:
# Extract reachable management IPs from a list of device dictionaries
reachable_ips = [dev["ip"] for dev in devices if dev.get("reachable", False)]
Modular Code Architecture: Functions
Functions allow engineers to organize procedural code into reusable, testable blocks. Defined using the def keyword, functions accept input arguments (positional or keyword) and return computed results via the return statement:
def build_interface_config(intf_name, vlan_id, mode="access"):
"""Generates standard Cisco IOS XE switchport configuration."""
commands = [
f"interface {intf_name}",
f"switchport mode {mode}"
]
if mode == "access":
commands.append(f"switchport access vlan {vlan_id}")
elif mode == "trunk":
commands.append(f"switchport trunk allowed vlan add {vlan_id}")
return "\n".join(commands)
Essential Network Automation Libraries: requests vs. ncclient
While Python's standard library provides raw socket and HTTP tools, enterprise network automation relies on specialized third-party libraries designed for networking workflows.
The requests Library for RESTful APIs
The requests library simplifies HTTP communication with controller platforms such as Cisco Catalyst Center, Cisco Catalyst SD-WAN Manager, and RESTCONF-enabled switches:
- HTTP Methods: Corresponds directly to REST API operations:
requests.get()to retrieve resources,requests.post()to create resources,requests.put()to replace resources,requests.patch()for partial updates, andrequests.delete()to remove resources. - Authentication: Supports basic authentication via
auth=("admin", "password")or custom token-based headers (headers={"X-Auth-Token": token}). - Response Handling: Returns a
Responseobject containing the HTTP status code (.status_code), raw response text (.text), and automated JSON decoding (.json()).
import requests
from requests.auth import HTTPBasicAuth
url = "https://10.1.1.1/restconf/data/ietf-interfaces:interfaces"
headers = {
"Accept": "application/yang-data+json",
"Content-Type": "application/yang-data+json"
}
response = requests.get(
url,
headers=headers,
auth=HTTPBasicAuth("cisco", "cisco123!"),
verify=False, # Bypasses self-signed certificate check in lab
timeout=10
)
if response.status_code == 200:
data = response.json()
print("Successfully retrieved interface configuration")
else:
print(f"Request failed with status: {response.status_code}")
The ncclient Library for NETCONF Operations
The ncclient library is a Python client library for NETCONF (RFC 6241 and RFC 6242), establishing SSH transport connections on TCP port 830 to exchange XML-encoded RPCs:
- Connection Management: Initiated using
manager.connect(), specifying parameters such ashost,port=830,username,password,hostkey_verify=False, anddevice_params={"name": "iosxe"}to negotiate device-specific capabilities. - RPC Operations: Supports standard NETCONF operations, including
<get-config>to pull configuration from datastores (such asrunningorcandidate),<edit-config>to modify target configurations,<commit>to apply candidate configurations on platforms supporting two-phase commits, and<get>to retrieve operational state. - XML Subtree and XPath Filtering: Employs XML subtree filters or XPath expressions to constrain queries so the device returns only requested subtrees rather than the entire configuration database.
from ncclient import manager
netconf_filter = """
<filter>
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<name>GigabitEthernet1</name>
</interface>
</interfaces>
</filter>
"""
with manager.connect(
host="10.1.1.1",
port=830,
username="admin",
password="cisco123!",
hostkey_verify=False,
device_params={"name": "iosxe"}
) as m:
reply = m.get_config(source="running", filter=("subtree", netconf_filter))
print(reply.xml)
Automation Library Comparison: requests vs. ncclient
| Dimension | requests Library | ncclient Library |
|---|---|---|
| Underlying Protocol | HTTP / HTTPS (REST and RESTCONF) | NETCONF over SSH (RFC 6242) |
| Default Transport Port | TCP 443 (HTTPS) or TCP 80 (HTTP) | TCP Port 830 (standard NETCONF) |
| Data Serialization | Typically JSON (also supports XML / plain text) | Strictly XML conforming to YANG schemas |
| Session Persistence | Stateless per request (unless requests.Session used) | Stateful SSH connection context with capability exchange |
| Primary Operations | GET, POST, PUT, PATCH, DELETE | <get>, <get-config>, <edit-config>, <commit>, <lock> |
| Target Platforms | Catalyst Center, SD-WAN Manager, RESTCONF devices | Cisco IOS XE, IOS XR, and NX-OS NETCONF subsystems |
| Transaction Support | Depends on REST API endpoint logic | Native datastore locking (<lock>) and candidate rollback |
A network engineer needs to safely retrieve an optional BGP neighbor description from a parsed JSON API response dictionary named 'neighbor_data'. If the key 'description' is absent, the script should return 'No Description Provided' without raising an uncaught exception. Which Python expression achieves this?
neighbor_data['description']
neighbor_data.pop('description', 'No Description Provided')
neighbor_data.get('description', 'No Description Provided')
neighbor_data['description'] if 'description' not in neighbor_data else None
When developing a Python script utilizing ncclient to retrieve interface configurations from a Cisco IOS XE router, which parameters and methods are used to establish the transport connection and query the running configuration datastore?
manager.connect() over TCP port 830 specifying the device_params, followed by m.get_config(source='running')
requests.get() over TCP port 443 with an Accept header for application/xml
manager.connect() over TCP port 22 using raw Telnet emulation, followed by m.edit_config(target='running')
socket.create_connection() to port 80, followed by m.dispatch('show running-config')
A network automation script executes requests.post() to create an administrative user on a controller. The HTTP response object has a status_code of 201. How should the script interpret this result?
The request was rejected due to invalid authentication credentials
The request was accepted and queued for background processing, but the resource is not yet created
The server encountered an internal database error during provisioning
The resource was successfully created on the server
Sections you finish are checked off in the contents.