Buy My Book on Amazon Buy My Book Direct (PDF) GitHub Profile LinkedIn Profile

Your First Python Class is a Device

Post 1 of series on Python OOP for Network Engineers

By Shahzad Qadir, Posted on: Oct. 19, 2025, 8:46 a.m.

Before we delve deeper into it, what is an Object. In simple terms, an object is something that has some properties and does some action. Like a Device is an object, it has some properties like it has a vendor, type, and mac address and it performs some action like if it's a router it performs Routing, if it's a switch it performs switching, and if it's an access point it provides wireless access. These devices might be performing other secondary actions apart from their main action, like a router can act as a TFTP server alongside it's normal routing action. But the idea here is to highlight a Device is an object as it perform some action and has some properties. In Python (and other programming languages too), properties are simply called properties and actions that an object performs are called methods. If you have done some functional programming in Python before, these methods are same as functions, they are just part of a class/object.

Objects share same properties with other objects, and also have their differences, like all Cisco devices share same vendor, all devices located at same location share the same location, and so. If we generalise this, all network devices are Devices and share some common properties and perform some actions that are common across all devices. All devices will have a mac address regardless of their device type. We can also say all network devices inherit from the basic Device type as they are devices at the end of the day. This is called inheritance and also called an is relationship - a router is a device it means a router inherits from a device.

So we know what is an Object and what is Inheritance, then what is a class? A class is a blue print which is used to construct an object in programming.

Let's write some classes and play around with objects, things will start becoming clearer.

In python you can define a class using this simple syntax.

class Device:
    pass

This an empty Device class, the pass tells python that we might write the properties and methods later and Python will not complain about it. So we got blueprint, we can create objects from it.

class Device:
    pass

if __name__ == "__main__":
    r1 = Device()
    print(r1)

In python any .py file can act as module and we can import different classes and functions from it and using if __name__ == "__main__" tells python if this .py file is run directly only then execute the code after if block otherwise if this .py file is used as a module to import code from it then ignore anything after if statement. As we will be running this file directly so if statement will hold true and the code inside if block will get executed.

Output
$ python3 oop_basics.py 
<__main__.Device object at 0x72f5a4a57670>

The code executed and Python has created an Object of Type Device and has saved it at memory location 0x72f5a4a57670.

We got our first class created, let's add some properties and methods to it.

class Device:
    def __init__(self, ip, vendor):
        self.ip = ip
        self.vendor = vendor

if __name__ == "__main__":
    R1 = Device(ip="10.10.10.1", vendor="Cisco")
    print(R1)
    print(f"IP: {R1.ip}")
    print(f"Vendor: {R1.vendor}")

Here, the things are going to be a little more interesting, soon we will have a discussion of class properties and object properties, class methods and object methods. In simple words class properties are the properties that belong to the class and don't require an object to be created, similarly the class methods are the methods that don't require creation of an object, they are just part of the class and we can use them without creating an object. We will discuss class properties and methods later in details.

In this code snippet that we have just written, we have one method called __init__, this method always exists in Python classes and is called when an object is created from a class. When an object is created, a reference to that object is passed as first argument in the __init__ method, we are calling this first argument self , it can be called anything but calling it self is a convention. So we just said first argument is the reference to the object that is being created then whenever we use self dot something we are referring to the object that is being created. When we write self.ip = ip we mean whatever ip is passed when creating this object must be assigned to the object as ip property of the object. By doing so we have created 2 object properties here; ip, and vendor.

Let's run this code and examine the output.

Output
<__main__.Device object at 0x73a6f685bf40>
IP: 10.10.10.1
Vendor: Cisco

We asked Python to print the object created from Device class, called R1, print IP of R1, and Vendor for R1. Output is what we expected.

When we print an object itself, Python calls a method called __str__ behind the scenes which prints the type of object, and it's memory location by default. We can change this behaviour, we can make python print type of object, Its vendor, and its IP. Let's do that

class Device:
    def __init__(self, ip, vendor, mac_address):
        self.ip = ip
        self.vendor = vendor

    def __str__(self):
        return f"<{type(self).__name__}: {self.vendor}, IP: {self.ip}>"

if __name__ == "__main__":
    R1 = Device(ip="10.10.10.1", vendor="Cisco")
    print(R1)
    print(f"IP: {R1.ip}")
    print(f"Vendor: {R1.vendor}")

We have changed __str__ to return a formatted string with device type - type(self).__name__ means check the type of object being created and from type only tell the name of class to which this object belongs.

Output
$ python3 oop_basics.py
<Device: Cisco, IP: 10.10.10.1>
IP: 10.10.10.1
Vendor: Cisco

As expected, now when we print object, we get the type of Object, Its vendor, and its IP address. It will be valuable when we put this class to work and create more objects from it and use it in our automation, printing only memory locations was not as useful for us.

The methods you have seen so far that start and end with double underscore __init__, __str__, and __name__ are called dunder methods and are run automatically behind the scenes. When we create a new object __init__ is runs automatically and create object variables that we specified, if we didn't define an __init__ method like when we created our first class without implementation, __init__ method still runs but don't do anything.

Similarly, __str__ is run when we print an object and it prints string representation of the object, when overriding __str__ method keep it mind that __str__ takes no parameters other than self and always returns a string.

There are other dunder methods in python too. When we want to compare two objects, __eq__ runs behind the scenes and if it returns True then objects will be deemed equal. Let's create 2 objects of our class, compare them and see what we get back.

class Device:
    def __init__(self, ip, vendor):
        self.ip = ip
        self.vendor = vendor

    def __str__(self):
        return f"<{type(self).__name__}: {self.vendor}, IP: {self.ip}>"

if __name__ == "__main__":
    R1 = Device(ip="10.10.10.1", vendor="Cisco")
    R2 = Device(ip="10.10.10.1", vendor="Cisco")
    print(R1)
    print(f"IP: {R1.ip}")
    print(f"Vendor: {R1.vendor}")
    print(R1 == R2)

Output

─$ python3 oop_basics.py 
<Device: Cisco, IP: 10.10.10.1>
IP: 10.10.10.1
Vendor: Cisco
False

Here we compared objects R1 and R2 of Device class, even though all properties of both objects are the same, Python interpreter still thinks they are different objects so it is returning False. The reason is __eq__ by default compare two object's memory location and if it is same then the objects are considered equal otherwise they are considered different.

But we can override this, we want to compare our devices and we will assume they are same devices if they have same IP address. Let's override default implementation of __eq__

class Device:
    def __init__(self, ip, vendor):
        self.ip = ip
        self.vendor = vendor

    def __str__(self):
        return f"<{type(self).__name__}: {self.vendor}, IP: {self.ip}>"

    def __eq__(self, other):
        if self.ip == other.ip:
            return True
        return False

if __name__ == "__main__":
    r1 = Device(ip="10.10.10.1", vendor="Cisco")
    r2 = Device(ip="10.10.10.1", vendor="Cisco")
    print(r1)
    print(f"IP: {r1.ip}")
    print(f"Vendor: {r1.vendor}")
    print(r1 == r2)

We have written our own implementation of __eq__ now and told Python deem our devices objects equal if they have same IP addresses. Let's review the output.

 $ python3 oop_basics.py
<Device: Cisco, IP: 10.10.10.1>
IP: 10.10.10.1
Vendor: Cisco
True

This time it returns True which is what we want.

Another more pythonic way of writing __eq__ method will be to remove redundant if statement and do comparison right at the return statement - it returns true if comparison returns True and returns False if comparison returns False.

class Device:
    def __init__(self, ip, vendor):
        self.ip = ip
        self.vendor = vendor

    def __str__(self):
        return f"<{type(self).__name__}: {self.vendor}, IP: {self.ip}>"

    def __eq__(self, other):
        return self.ip == other.ip

if __name__ == "__main__":
    r1 = Device(ip="10.10.10.1", vendor="Cisco")
    r2 = Device(ip="10.10.10.1", vendor="Cisco")
    print(r1)
    print(f"IP: {r1.ip}")
    print(f"Vendor: {r1.vendor}")
    print(r1 == r2)

Both are working solutions and its a matter of choice how you want to write it, you can compare at return or you can write an if statement to do that.

Practice

  1. Override __str__ to return where ip is the ip of device object.
  2. Add another object property called mac_address, and change __eq__ to return True if mac_address of both devices is same.

In the next post, we will discuss more about inheritance and will create some classes that will inherit from our Device class. Till then, thanks for stopping by, send me a message on LinkedIn if you get stuck or want to catch up.