Unit 9 — Classes — Modeling Network Devices as Objects
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 (usuallyself)__next__— called on each loop step; returns the next value or raisesStopIterationwhen 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
selfas the first argument - Include a one-line docstring on every method
- Inheritance: use
class Child(Parent)andsuper()to extend a class - Instance variables belong to one object; class variables are shared across all
_namesignals a private or internal attribute- Implement
__iter__and__next__to make a class iterable withfor - Use
yieldinside__iter__for a simpler iterator — Python handles the rest