Classes In Python — PBA Institute Tutorial
Chapter 17 · Python Programming Series
12 min read Beginner

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 take cls; static methods take neither.
  • Use @property to expose attributes through getter/setter methods.
  • Override __init__ to configure each new instance.
Classes — Syntax
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__ with self.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 self as the first parameter. Operate on the current instance. Most common method type.
  • Class methods: Decorated with @classmethod; take cls. Used for alternative constructors and factory methods.
  • Static methods: Decorated with @staticmethod; take neither self nor cls. Behave like plain functions but live in the class namespace.
  • Properties: Use @property to create computed read-only attributes; pair with @x.setter for validation logic. Lets you change implementation without breaking the public API.

Code Examples

Example 1 — A Simple Class
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)))
Output 5.0
Example 2 — Class vs Instance Attributes
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)
Output 0 1 2 total= 3
Example 3 — classmethod (Alternative Constructor)
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__)
Output {'d': 12, 'm': 9, 'y': 2024}
Example 4 — staticmethod
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))
Output True
False
Example 5 — Property with Validation
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)
Output 1200
Example 6 — __str__ and __repr__
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))
Output Book: Python
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 @property over getter/setter methods.
  • Use @dataclass for 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 without self causes 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 Movie with title, year, rating. Add a method that returns 'classic' if year < 2000.
  • Problem 2: Build Calculator with class methods for add, subtract, multiply, divide.
  • Problem 3: Create a Stack class 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 Time class 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 @classmethod and @staticmethod?
  • Q2. Why use the @property decorator?
  • Q3. Difference between class attribute and instance attribute?
  • Q4. What does self represent?
  • 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

Next

Go to Next Chapter