Object-Oriented Python
Build classes and objects to model security tools — encapsulate scanner logic, extend base classes, and use dunder methods.
Learning Objectives
- → Define classes with __init__ and instance methods
- → Use encapsulation to hide internal state
- → Implement inheritance and method overriding
- → Use dunder methods (__str__, __repr__, __len__)
- → Model a real security tool using OOP principles
Classes and Objects
class Scanner:
def __init__(self, host, timeout=1.0):
self.host = host
self.timeout = timeout
self._results = {} # 'private' by convention
def scan_port(self, port):
import socket
try:
with socket.create_connection((self.host, port), self.timeout):
self._results[port] = "open"
except OSError:
self._results[port] = "closed"
def get_open_ports(self):
return [p for p, s in self._results.items() if s == "open"]
def __str__(self):
return f"Scanner({self.host}) — {len(self._results)} ports checked"
def __len__(self):
return len(self._results)
s = Scanner("127.0.0.1")
s.scan_port(22)
s.scan_port(80)
print(s) # Scanner(127.0.0.1) — 2 ports checked
print(s.get_open_ports())
Class vs Instance Variables
class Target:
count = 0 # class variable — shared by all instances
def __init__(self, ip):
Target.count += 1
self.ip = ip # instance variable
self.id = Target.count
t1 = Target("10.0.0.1")
t2 = Target("10.0.0.2")
print(Target.count) # 2
print(t1.id, t2.id) # 1 2
Inheritance
class BaseExploit:
def __init__(self, target):
self.target = target
def connect(self):
raise NotImplementedError
def run(self):
self.connect()
self.execute()
class SSHBruteforce(BaseExploit):
def __init__(self, target, wordlist):
super().__init__(target)
self.wordlist = wordlist
def connect(self):
print(f"Connecting to {self.target} via SSH")
def execute(self):
for word in self.wordlist:
print(f" Trying: {word}")
class FTPBruteforce(BaseExploit):
def connect(self):
print(f"Connecting to {self.target} via FTP")
def execute(self):
print("Running FTP attack")
Properties (Getters/Setters)
class Target:
def __init__(self, ip):
self._ip = ip
@property
def ip(self):
return self._ip
@ip.setter
def ip(self, value):
parts = value.split(".")
if len(parts) != 4:
raise ValueError(f"Invalid IP: {value}")
self._ip = value
t = Target("192.168.1.1")
print(t.ip) # getter
t.ip = "10.0.0.1" # setter with validation
Dunder Methods
| Method | Purpose |
|---|---|
__init__ |
Constructor |
__str__ |
Human-readable string (print) |
__repr__ |
Developer representation |
__len__ |
len(obj) support |
__eq__ |
Equality comparison |
__lt__ |
Less-than (enables sorting) |
__iter__ |
Makes object iterable |
Mini Project – ScanReport Class
class ScanReport:
def __init__(self, host):
self.host = host
self._data = {}
def add_result(self, port, status, service=""):
self._data[port] = {"status": status, "service": service}
def open_ports(self):
return [p for p, d in self._data.items() if d["status"] == "open"]
def to_dict(self):
return {"host": self.host, "results": self._data}
def __repr__(self):
return f"ScanReport(host={self.host!r}, ports={len(self._data)})"
def __len__(self):
return len(self.open_ports())
r = ScanReport("192.168.1.1")
r.add_result(22, "open", "SSH")
r.add_result(80, "open", "HTTP")
r.add_result(443, "closed", "HTTPS")
print(r)
print(f"Open ports: {r.open_ports()}")
Implement the Scanner class from the lesson. Add a scan_range(start, end) method that calls scan_port for each port in the range. Test it against localhost.
What method is called when an object is created?
What keyword accesses the current instance inside a method?
Create a BaseTool class with __init__(self, target) and a run() method that calls execute(). Then create NmapTool and DirbTool subclasses that override execute(). Show polymorphism by calling .run() on both.
What function calls the parent class's __init__?
Implement ScanReport. Add a to_json() method that returns json.dumps(self.to_dict()). Add __eq__ that compares hosts. Create two reports for the same host and verify equality.
Which dunder method enables == comparison between objects?