Designing Real-World Objects With Classes
A class is the blueprint Python uses to manufacture objects. From the simplest data holder to a complex domain model, classes give you a clean way to combine state (attributes) and behaviour (methods). This lesson dives deeper than 'hello-class' — we'll cover instance vs class attributes, methods, class methods, static methods, properties, and best design practices.
Overview
Blueprints
One class → many objects, each with its own data.
Stateful Methods
Methods can read and modify the object's own data.
Factory Methods
Class methods build instances in alternative ways (e.g. from CSV).
Validation
Properties hide setters that validate before storing.
Namespaces
Group related behaviours and constants in one tidy unit.
Syntax
- Define with
class ClassName:. - Instance methods take
self; class methods takecls; static methods take neither. - Use
@propertyto expose attributes through getter/setter methods. - Override
__init__to configure each new instance.
class Employee:
company = "PBA Institute" # class attribute
def __init__(self, name, salary):
self.name = name # instance attribute
self._salary = salary
@property # getter
def salary(self):
return self._salary
@salary.setter
def salary(self, value):
if value < 0:
raise ValueError("negative salary")
self._salary = value
@classmethod
def from_csv(cls, line):
n, s = line.split(",")
return cls(n, float(s))
@staticmethod
def is_valid_name(name):
return name.isalpha()
Detailed Explanation
- Instance attributes: Set inside
__init__withself.x = .... Unique per instance — modifying on one doesn't affect another. - Class attributes: Defined directly in the class body. Shared by all instances. Useful for constants and counters.
- Instance methods: Take
selfas the first parameter. Operate on the current instance. Most common method type. - Class methods: Decorated with
@classmethod; takecls. Used for alternative constructors and factory methods. - Static methods: Decorated with
@staticmethod; take neitherselfnorcls. Behave like plain functions but live in the class namespace. - Properties: Use
@propertyto create computed read-only attributes; pair with@x.setterfor validation logic. Lets you change implementation without breaking the public API.
Code Examples
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def distance(self, other):
return ((self.x-other.x)**2 + (self.y-other.y)**2) ** 0.5
print(Point(0,0).distance(Point(3,4)))
class Counter:
total = 0 # class
def __init__(self):
self.id = Counter.total
Counter.total += 1
a, b, c = Counter(), Counter(), Counter()
print(a.id, b.id, c.id, "total=", Counter.total)
class Date:
def __init__(self, d, m, y):
self.d, self.m, self.y = d, m, y
@classmethod
def from_string(cls, s):
d, m, y = map(int, s.split("/"))
return cls(d, m, y)
print(Date.from_string("12/09/2024").__dict__)
class MathUtil:
@staticmethod
def is_prime(n):
return n > 1 and all(n % i for i in range(2, int(n**0.5)+1))
print(MathUtil.is_prime(17))
print(MathUtil.is_prime(18))
False
class Account:
def __init__(self, bal=0):
self._bal = bal
@property
def balance(self):
return self._bal
@balance.setter
def balance(self, v):
if v < 0: raise ValueError("negative")
self._bal = v
a = Account(1000)
a.balance = 1200
print(a.balance)
class Book:
def __init__(self, title):
self.title = title
def __str__(self):
return f"Book: {self.title}"
def __repr__(self):
return f"Book(title={self.title!r})"
b = Book("Python")
print(b)
print(repr(b))
Book(title='Python')
Real-World Use Cases
User Model
Each user is an object with profile data and methods like activate, deactivate.
Cart
A Cart class holds items and exposes add/remove/total methods.
Payment Gateway
Wrap third-party API calls behind a clean class.
Email Service
EmailMessage class with send(), attach(), validate() methods.
Sensor Classes
Each sensor type is a subclass with read() and calibrate().
Form Models
Django and Pydantic models are classes describing data shapes.
Notes & Pro Tips
- Use class attributes only for constants — instance state belongs in
__init__. - Prefer
@propertyover getter/setter methods. - Use
@dataclassfor plain data holders. - Avoid storing too much state — small focused classes age well.
- Define
__repr__for debugging output; it shows in lists and the REPL. - Stick to one responsibility per class — split when it grows too large.
Common Mistakes
- Using mutable class attributes: shared list/dict at class level surprises instances.
- Forgetting
self: defining a method withoutselfcauses errors. - Setting attributes outside
__init__: harder to discover the object's state. - Static methods that should be classmethods: if you need
cls, choose classmethod. - Misusing inheritance: when behaviour, not identity, varies — composition is better.
- No
__repr__: default<object at 0x...>printout is unhelpful.
Practice Problems
- Problem 1: Create a class
Moviewith title, year, rating. Add a method that returns 'classic' if year < 2000. - Problem 2: Build
Calculatorwith class methods for add, subtract, multiply, divide. - Problem 3: Create a
Stackclass with push, pop, peek, and is_empty methods. - Problem 4: Use a property to expose a temperature attribute in Celsius and a setter that accepts both C and F.
- Problem 5: Implement
Timeclass with hour, minute, second and a __str__ that prints HH:MM:SS. - Problem 6: Build a Student class that loads from a CSV line using a classmethod factory.
Interview Questions
- Q1. What is the difference between
@classmethodand@staticmethod? - Q2. Why use the
@propertydecorator? - Q3. Difference between class attribute and instance attribute?
- Q4. What does
selfrepresent? - Q5. Why might you override
__repr__instead of (or in addition to)__str__? - Q6. Explain how dataclasses simplify class definitions.
Frequently Asked Questions
- Q1: What is the difference between class and instance?
A class is the blueprint; an instance is one concrete object created from it. - Q2: What does self mean?
self is the conventional name for the current instance, passed automatically as the first argument of every instance method. - Q3: What is a classmethod?
A method bound to the class rather than an instance. Useful for alternative constructors like from_string or from_csv. - Q4: What is a staticmethod?
A regular function that lives inside a class for namespace organisation. It takes neither self nor cls. - Q5: What are properties?
They expose attribute-like access backed by getter/setter methods — great for validation and computed values. - Q6: When should I use @dataclass?
When the class is mostly about holding data. @dataclass auto-generates __init__, __repr__, __eq__, and more.
Summary
A class organises state and behaviour into a coherent unit. By picking the right method type, using properties for validation, and choosing composition over needless inheritance, you build classes that are clear, robust, and easy to evolve. Combine with dataclasses and dunder methods to write modern, idiomatic Python that scales from one-liners to large systems.
Continue Learning
Previous
Go to Previous Chapter