⚡ IT Wisdom We pushed to prod on Friday. This is our origin story.
Skilled Practitioner
4249 XP 1751 to Expert
Units
Whetting Your Appetite — Why Python for Networking? Using the Python Interpreter An Informal Introduction to Python — Network Basics Control Flow — Making Decisions in Network Scripts Data Structures — Modeling Network Inventories Modules — Organizing Your Network Scripts Input and Output — Reading and Writing Network Data Errors and Exceptions — Building Reliable Network Scripts Classes — Modeling Network Devices as Objects Standard Library Part I — Tools Every Network Script Needs Standard Library Part II — Logging, Formatting, and Counting Virtual Environments and Packages — Isolating Your Dependencies Capstone — Build a Network Inventory Tool

What Is a Class?

A class is a blueprint for creating objects. An object is an instance of that blueprint — it has its own data (attributes) and behaviour (methods).

Think of it like a Cisco device template in DNA Center. The template defines what a device looks like and what it can do. Each device you deploy from it is an instance — same structure, different values.


Creating a Class

class NetworkDevice:
    def __init__(self, name, ip, device_type, site, vlan):
        self.name        = name
        self.ip          = ip
        self.device_type = device_type
        self.site        = site
        self.vlan        = vlan

__init__ is the constructor — it runs automatically when you create an instance. self refers to the specific object being created. Every attribute is set with self.attribute = value.

Create instances:

sw = NetworkDevice("sw-core-01", "10.1.1.1", "switch", "HQ", 10)
print(sw.name)   # sw-core-01
print(sw.vlan)   # 10

Methods — Behaviour

A method is a function defined inside a class. It always receives self as its first argument:

class NetworkDevice:
    def __init__(self, name, ip, device_type, site, vlan):
        self.name        = name
        self.ip          = ip
        self.device_type = device_type
        self.site        = site
        self.vlan        = vlan

    def get_summary(self):
        """Return a one-line description of this device."""
        return f"{self.name} ({self.device_type}) — {self.ip} — Site: {self.site} — VLAN: {self.vlan}"

Inheritance — Specialising a Class

A subclass inherits everything from its parent class and can add or override behaviour:

class CiscoDevice(NetworkDevice):
    def __init__(self, name, ip, site, ios_version):
        super().__init__(name, ip, "cisco", site, vlan=1)
        self.ios_version = ios_version

    def get_summary(self):
        """Return a summary including the IOS version."""
        return f"{super().get_summary()} — IOS: {self.ios_version}"

super() calls the parent class's method. CiscoDevice is still a NetworkDevice — it just has an extra attribute and a richer summary.


Class Variables vs Instance Variables

Instance variables (defined in __init__) belong to each object. Class variables belong to the class itself and are shared across all instances:

class NetworkDevice:
    vendor = "Cisco"   # class variable — shared by all instances

    def __init__(self, name, ip):
        self.name = name   # instance variable — unique per object
        self.ip   = ip

Private Variables

Prefix an attribute with _ to signal it is internal — not intended for direct access from outside the class:

class NetworkDevice:
    def __init__(self, name, ip):
        self.name  = name
        self._raw_ip = ip   # internal use only

    def get_ip(self):
        """Return the device IP address."""
        return self._raw_ip

This is a convention, not enforcement — Python does not block access, but the underscore tells other developers not to rely on it.


Iterators — Making a Class Iterable

An iterator is an object you can loop over with for. To make your class iterable, implement two methods:

  • __iter__ — called when iteration starts; returns the iterator object (usually self)
  • __next__ — called on each loop step; returns the next value or raises StopIteration when done
class DeviceCounter:
    """Counts from 1 up to a limit — demonstrates the iterator protocol."""

    def __init__(self, limit):
        self.limit   = limit
        self._current = 0

    def __iter__(self):
        return self

    def __next__(self):
        self._current += 1
        if self._current > self.limit:
            raise StopIteration
        return self._current

for n in DeviceCounter(3):
    print(n)
# 1
# 2
# 3

In practice, generators (below) are the easier way to build iterables.


Generators — Iterators Made Simple

A generator is a function that uses yield instead of return. Each time yield is reached, the value is produced and the function pauses. The next loop step resumes it from where it left off:

def online_devices(devices):
    """Yield only devices whose status is online."""
    for device in devices:
        if device["status"] == "online":
            yield device["name"]

inventory = [
    {"name": "sw-core-01",  "status": "online"},
    {"name": "sw-access-02","status": "offline"},
    {"name": "rtr-edge-01", "status": "online"},
]

for name in online_devices(inventory):
    print(name)
# sw-core-01
# rtr-edge-01

A generator only computes values when asked — useful when working with large device inventories you do not want to load all at once.

You can also use yield inside __iter__ to make a class iterable without implementing __next__:

class DeviceInventory:
    def __init__(self, devices):
        self._devices = devices

    def __iter__(self):
        for device in self._devices:
            yield device

Summary

  • A class is a blueprint; an object is an instance of that blueprint
  • __init__ sets up the object's attributes when it is created
  • Methods are functions inside a class — they always take self as the first argument
  • Include a one-line docstring on every method
  • Inheritance: use class Child(Parent) and super() to extend a class
  • Instance variables belong to one object; class variables are shared across all
  • _name signals a private or internal attribute
  • Implement __iter__ and __next__ to make a class iterable with for
  • Use yield inside __iter__ for a simpler iterator — Python handles the rest