Chapter 9: Classes
Classes model real-world things. They combine data (attributes) and behavior (methods).
Creating a Class
class Server:
"""Represents a server in the infrastructure."""
def __init__(self, hostname, ip): (1)
"""Initialize server attributes."""
self.hostname = hostname (2)
self.ip = ip
self.status = 'stopped' (3)
def start(self): (4)
"""Start the server."""
self.status = 'running'
print(f"{self.hostname} started")
def stop(self):
"""Stop the server."""
self.status = 'stopped'
print(f"{self.hostname} stopped")
| 1 | init runs automatically when creating instance |
| 2 | self.attribute stores data on the instance |
| 3 | Default value - no argument needed |
| 4 | Methods are functions that belong to the class |
Creating Instances
web = Server('web-01', '10.0.1.10') (1)
db = Server('db-01', '10.0.2.10')
print(web.hostname) # 'web-01'
print(web.ip) # '10.0.1.10'
print(web.status) # 'stopped'
| 1 | Python calls init with 'web-01' and '10.0.1.10' |
Calling Methods
web.start() # web-01 started
print(web.status) # 'running'
web.stop() # web-01 stopped
Multiple Instances
Each instance is independent:
web = Server('web-01', '10.0.1.10')
db = Server('db-01', '10.0.2.10')
web.start()
print(web.status) # 'running'
print(db.status) # 'stopped' - unaffected
Modifying Attributes
Direct Access
web.status = 'maintenance'
Through Methods
Better control - can add validation:
class Server:
# ...
def set_status(self, status):
"""Set status with validation."""
valid = ['running', 'stopped', 'maintenance']
if status in valid:
self.status = status
else:
print(f"Invalid status: {status}")
Incrementing
class Server:
def __init__(self, hostname, ip):
self.hostname = hostname
self.ip = ip
self.request_count = 0
def log_request(self):
"""Increment request counter."""
self.request_count += 1
Inheritance
Create specialized versions of existing classes.
class WebServer(Server): (1)
"""A web server with HTTP-specific features."""
def __init__(self, hostname, ip, port=80):
super().__init__(hostname, ip) (2)
self.port = port (3)
self.ssl = port == 443
def serve(self):
"""Start serving HTTP requests."""
protocol = 'https' if self.ssl else 'http'
print(f"Serving at {protocol}://{self.ip}:{self.port}")
| 1 | WebServer inherits from Server |
| 2 | super().init() calls parent’s init |
| 3 | Add child-specific attributes |
nginx = WebServer('web-01', '10.0.1.10', 443)
nginx.start() # Inherited from Server
nginx.serve() # WebServer-specific
print(nginx.ssl) # True
Overriding Methods
Child can replace parent methods:
class WebServer(Server):
# ...
def start(self):
"""Start with HTTP-specific message."""
self.status = 'running'
print(f"{self.hostname} listening on port {self.port}")
Now WebServer.start() replaces Server.start().
Composition
Use instances as attributes - "has a" relationship.
class Database:
"""Database connection settings."""
def __init__(self, host, port=5432):
self.host = host
self.port = port
self.connected = False
def connect(self):
self.connected = True
print(f"Connected to {self.host}:{self.port}")
class Application:
"""Application with database."""
def __init__(self, name, db_host):
self.name = name
self.database = Database(db_host) (1)
def start(self):
self.database.connect()
print(f"{self.name} running")
| 1 | Application has a Database instance |
app = Application('myapp', '10.0.2.10')
app.start()
# Connected to 10.0.2.10:5432
# myapp running
app.database.connected # True
Importing Classes
Single Class
# server.py
class Server:
# ...
# main.py
from server import Server
web = Server('web-01', '10.0.1.10')
Multiple Classes
# Put related classes in one module
from infrastructure import Server, WebServer, Database
Or import the module:
import infrastructure as infra
web = infra.WebServer('web-01', '10.0.1.10')
db = infra.Database('db-01')
Standard Library Classes
Python includes useful classes. Example with random:
from random import randint, choice
# Random integer between 1 and 100
port = randint(1024, 65535)
# Random choice from list
server = choice(['web-01', 'web-02', 'web-03'])
Class Patterns
Service with Multiple States
class Service:
STATES = ['stopped', 'starting', 'running', 'stopping']
def __init__(self, name):
self.name = name
self.state = 'stopped'
def transition(self, new_state):
if new_state in self.STATES:
print(f"{self.name}: {self.state} -> {new_state}")
self.state = new_state
Configuration Object
class Config:
def __init__(self, **settings):
for key, value in settings.items():
setattr(self, key, value) (1)
def __repr__(self): (2)
items = [f"{k}={v!r}" for k, v in self.__dict__.items()]
return f"Config({', '.join(items)})"
| 1 | Dynamically set attributes from kwargs |
| 2 | Custom string representation |
cfg = Config(host='10.0.1.10', port=443, ssl=True)
print(cfg.host) # 10.0.1.10
print(cfg) # Config(host='10.0.1.10', port=443, ssl=True)
Styling Classes
class ClassName: (1)
"""Class docstring."""
def __init__(self, param):
"""Initialize attributes."""
self.attribute = param
def method_name(self): (2)
"""Method docstring."""
pass
| 1 | Class names: CamelCase |
| 2 | Method names: snake_case |
Two blank lines before and after class definitions.
Quick Reference
| Concept | Code |
|---|---|
Define class |
|
Constructor |
|
Instance attribute |
|
Method |
|
Create instance |
|
Access attribute |
|
Call method |
|
Inheritance |
|
Call parent init |
|
Import class |
|
Exercises
9-1. Server
Create Server class with hostname, ip, status. Add describe() method.
9-2. Database Server
Create DatabaseServer(Server) with additional engine attribute (postgres, mysql).
9-3. Service Counter
Create Service class tracking request count. Methods: handle_request(), get_stats().
9-4. User
Create User class with username, login_attempts. Methods: increment_login_attempts(), reset_login_attempts().
9-5. Composition
Create Cluster class containing a list of Server instances. Method: status() showing all servers.
9-6. Module
Store your classes in infrastructure.py. Import and use them.
Summary
-
Classes combine data (attributes) and behavior (methods)
-
initinitializes new instances -
selfrefers to the current instance -
Inheritance: child classes extend parent classes
-
super()calls parent class methods -
Composition: classes can contain other class instances
-
CamelCase for class names, snake_case for methods
Next: Working with files and handling exceptions.