Classes in Python
A class is a blueprint or template for creating objects. It defines the structure and behavior that objects of that class will have. Classes are fundamental to Object-Oriented Programming (OOP) and provide a way to organize code into reusable, logical units.
Basic Class Definition
class Person:
"""A simple class representing a person."""
def __init__(self, name, age):
self.name = name
self.age = age
def greet(self):
return f"Hello, my name is {self.name} and I am {self.age} years old."
# Creating an instance
person = Person("Alice", 30)
print(person.greet()) # Output: Hello, my name is Alice and I am 30 years old.
Class Components
1. Class Name
- Follows Python naming conventions (PascalCase)
- Should be descriptive and meaningful
2. Class Variables (Class Attributes)
- Shared among all instances of the class
- Defined at the class level
class Car:
# Class variable
wheels = 4
def __init__(self, brand, model):
self.brand = brand
self.model = model
car1 = Car("Toyota", "Camry")
car2 = Car("Honda", "Civic")
print(car1.wheels) # 4
print(car2.wheels) # 4
print(Car.wheels) # 4
3. Instance Variables (Instance Attributes)
- Unique to each instance
- Defined in
__init__method usingself
class Student:
def __init__(self, name, student_id):
self.name = name # Instance variable
self.student_id = student_id # Instance variable
self.grades = [] # Instance variable
4. Methods
- Functions defined within a class
- Can be instance methods, class methods, or static methods
Types of Methods
Instance Methods
- Most common type of method
- Take
selfas the first parameter - Can access and modify instance attributes
class BankAccount:
def __init__(self, balance):
self.balance = balance
def deposit(self, amount):
self.balance += amount
return f"Deposited ${amount}. New balance: ${self.balance}"
def withdraw(self, amount):
if self.balance >= amount:
self.balance -= amount
return f"Withdrew ${amount}. New balance: ${self.balance}"
else:
return "Insufficient funds"
Class Methods
- Use
@classmethoddecorator - Take
clsas the first parameter - Can access class variables but not instance variables
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
@classmethod
def from_string(cls, date_string):
year, month, day = map(int, date_string.split('-'))
return cls(year, month, day)
@classmethod
def today(cls):
import datetime
today = datetime.date.today()
return cls(today.year, today.month, today.day)
# Using class methods
date1 = Date.from_string("2023-12-25")
date2 = Date.today()
Static Methods
- Use
@staticmethoddecorator - Don’t take
selforclsas parameters - Cannot access class or instance variables
class MathUtils:
@staticmethod
def add(x, y):
return x + y
@staticmethod
def multiply(x, y):
return x * y
@staticmethod
def is_even(num):
return num % 2 == 0
# Using static methods
result = MathUtils.add(5, 3) # 8
is_even = MathUtils.is_even(10) # True
Access Modifiers
Python doesn’t have strict access modifiers like other languages, but uses naming conventions:
Public Attributes
- No special prefix
- Accessible from anywhere
class Person:
def __init__(self, name):
self.name = name # Public attribute
Protected Attributes
- Single underscore prefix
_ - Convention indicating “internal use”
class Person:
def __init__(self, name):
self._name = name # Protected attribute (convention)
Private Attributes
- Double underscore prefix
__ - Name mangling prevents direct access
class Person:
def __init__(self, name):
self.__name = name # Private attribute
def get_name(self):
return self.__name
person = Person("Alice")
# person.__name # AttributeError
print(person.get_name()) # Alice
Constructor and Destructor
__init__ Method (Constructor)
- Called automatically when creating an instance
- Used for initialization
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
self.area = width * height # Calculate area during initialization
__del__ Method (Destructor)
- Called when object is about to be destroyed
- Used for cleanup
class FileHandler:
def __init__(self, filename):
self.filename = filename
self.file = open(filename, 'r')
def __del__(self):
if hasattr(self, 'file'):
self.file.close()
print(f"File {self.filename} closed")
Class Inheritance
Classes can inherit from other classes:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
pass
class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says Meow!"
dog = Dog("Buddy")
cat = Cat("Whiskers")
print(dog.speak()) # Buddy says Woof!
print(cat.speak()) # Whiskers says Meow!
Special Methods (Magic Methods)
Classes can define special methods for custom behavior:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"Point({self.x}, {self.y})"
def __repr__(self):
return f"Point({self.x}, {self.y})"
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
p1 = Point(1, 2)
p2 = Point(3, 4)
p3 = p1 + p2
print(p3) # Point(4, 6)
Summary Table
| Component | Description | Example |
|---|---|---|
| Class Variable | Shared among all instances | wheels = 4 |
| Instance Variable | Unique to each instance | self.name = name |
| Instance Method | Method that uses self |
def greet(self): |
| Class Method | Method that uses cls |
@classmethod def create(cls): |
| Static Method | Method that uses neither | @staticmethod def utility(): |
| Constructor | __init__ method |
def __init__(self, name): |
| Destructor | __del__ method |
def __del__(self): |
Key Interview Points
- Classes are blueprints for creating objects
selfrefers to the instance of the class__init__is the constructor method- Class variables are shared, instance variables are unique
- Methods can be instance, class, or static
- Inheritance allows code reuse and hierarchy
- Magic methods customize object behavior
- Access modifiers are conventions, not enforced
Understanding classes is fundamental to Python OOP and essential for writing maintainable, reusable code!
Interview angle
- “Class attribute versus instance attribute?” — a class attribute is shared by every instance; assigning to
self.xcreates an instance attribute that shadows it. The classic bug is a mutable class attribute like a list, where every instance appends to the same object. - “
@staticmethod,@classmethod, or a plain method?” — plain when it usesself;@classmethodwhen it needs the class, which is what makes alternative constructors work correctly under inheritance (clsis the subclass);@staticmethodwhen it uses neither and is just namespaced with the class. - “Why does
@classmethodmatter for factories?” —cls(...)constructs the actual subclass, soSubclass.from_json(...)returns aSubclass. Hardcoding the class name in a@staticmethodreturns the base class and silently breaks inheritance. - “What does a dataclass save you?” — generated
__init__,__repr__and__eq__, plusfrozen=Truefor immutability and hashability. Usefield(default_factory=list)for mutable defaults — a bare[]is the same shared-object bug as in a function signature.