🕯️ Magic Note
Python’s class syntax is intentionally minimal. There are no access modifiers like public or private (though conventions exist). The philosophy is trust over restriction. Programmers are adults. They will follow conventions. This simplicity makes Python classes easy to write and read.
Python
# Basic class definition
class Car:
pass # Empty class (placeholder)
# Creating an instance (object)
my_car = Car()
print(type(my_car)) # <class ‘__main__.Car’>
Python
# Adding attributes dynamically (works but not recommended)
class Car:
pass
my_car = Car()
my_car.brand = “Toyota” # Add attribute on the fly
my_car.year = 2020
print(my_car.brand) # Toyota
your_car = Car()
# print(your_car.brand) # AttributeError! This car has no brand.
Python
class Car:
def __init__(self, brand, year, color):
self.brand = brand
self.year = year
self.color = color
self.mileage = 0 # Default value, not passed as parameter
# Creating objects (__init__ is called automatically)
car1 = Car(“Toyota”, 2020, “Red”)
car2 = Car(“Honda”, 2022, “Blue”)
print(car1.brand) # Toyota
print(car1.mileage) # 0
print(car2.color) # Blue
🕯️ Magic Note
The self parameter is automatically passed by Python. It refers to the specific object being created. When you write self.brand = brand, you are storing the value in the object’s instance dictionary.
Python
class Car:
def __init__(self, brand, year, color):
self.brand = brand
self.year = year
self.color = color
self.mileage = 0
def drive(self, kilometers):
self.mileage += kilometers
print(f”{self.brand} drove {kilometers} km. Total mileage: {self.mileage}”)
def repaint(self, new_color):
print(f”Changing {self.brand} from {self.color} to {new_color}”)
self.color = new_color
def get_info(self):
return f”{self.brand} ({self.year}) – {self.color} – {self.mileage} km”
# Using the methods
my_car = Car(“Tesla”, 2023, “White”)
my_car.drive(150) # Tesla drove 150 km. Total mileage: 150
my_car.repaint(“Black”) # Changing Tesla from White to Black
print(my_car.get_info()) # Tesla (2023) – Black – 150 km
Python
class Car:
# Class attribute (shared by all cars)
vehicle_type = “Passenger Vehicle”
total_cars_created = 0
def __init__(self, brand, year, color):
self.brand = brand
self.year = year
self.color = color
self.mileage = 0
Car.total_cars_created += 1 # Increment class attribute
car1 = Car(“Toyota”, 2020, “Red”)
car2 = Car(“Honda”, 2021, “Blue”)
car3 = Car(“Ford”, 2022, “Green”)
# Access class attribute via class or instance
print(Car.vehicle_type) # Passenger Vehicle
print(car1.vehicle_type) # Passenger Vehicle (inherited)
print(Car.total_cars_created) # 3
Python
class Car:
total_cars = 0
def __init__(self, brand):
self.brand = brand
Car.total_cars += 1
# Instance method (operates on a specific car)
def display(self):
print(f”This car is a {self.brand}”)
# Class method (operates on the class)
@classmethod
def get_total_cars(cls):
return f”Total cars created: {cls.total_cars}”
# Class method as alternative constructor
@classmethod
def from_string(cls, car_string):
brand = car_string.split(“-“)[0]
return cls(brand) # Creates a new Car instance
# Using class method
print(Car.get_total_cars()) # Total cars created: 0
car1 = Car(“Toyota”)
car2 = Car(“Honda”)
print(Car.get_total_cars()) # Total cars created: 2
# Alternative constructor
car3 = Car.from_string(“Tesla-2023-Electric”)
print(car3.brand) # Tesla
🕯️ Magic Note
Class methods are useful for alternative constructors and for operations that involve the class as a whole, not individual instances. They are called on the class (Car.get_total_cars()) not on instances.
Python
class MathUtils:
@staticmethod
def is_even(number):
return number % 2 == 0
@staticmethod
def is_prime(number):
if number < 2:
return False
for i in range(2, int(number ** 0.5) + 1):
if number % i == 0:
return False
return True
# Call static methods on the class (no instance needed)
print(MathUtils.is_even(10)) # True
print(MathUtils.is_prime(17)) # True
# In a more relevant example
class Car:
@staticmethod
def validate_year(year):
return 1886 <= year <= 2025 # First car invented in 1886
def __init__(self, brand, year):
if not Car.validate_year(year):
raise ValueError(f”Invalid year: {year}”)
self.brand = brand
self.year = year
Python
class Car:
def __init__(self, brand, year, color):
self.brand = brand
self.year = year
self.color = color
# __str__: user-friendly string (used by print())
def __str__(self):
return f”{self.color} {self.brand} from {self.year}”
# __repr__: developer-friendly string (used by debugger)
def __repr__(self):
return f”Car(brand='{self.brand}’, year={self.year}, color='{self.color}’)”
my_car = Car(“Tesla”, 2023, “White”)
print(my_car) # White Tesla from 2023 (uses __str__)
print(repr(my_car)) # Car(brand=’Tesla’, year=2023, color=’White’) (uses __repr__)
🕯️ Magic Note
If you define only __repr__, it will be used for both str() and repr() as a fallback. But it is good practice to define both, especially for classes used in debugging.
Python
class LibraryBook:
# Class attribute
library_name = “Feloriya Public Library”
def __init__(self, title, author, isbn):
# Instance attributes
self.title = title
self.author = author
self.isbn = isbn
self.is_checked_out = False
self.checkout_history = []
def check_out(self, borrower_name):
if self.is_checked_out:
return False, f”‘{self.title}’ is already checked out”
self.is_checked_out = True
self.checkout_history.append(borrower_name)
return True, f”{borrower_name} checked out ‘{self.title}'”
def return_book(self):
if not self.is_checked_out:
return False, f”‘{self.title}’ was not checked out”
self.is_checked_out = False
return True, f”‘{self.title}’ has been returned”
def get_info(self):
status = “Checked Out” if self.is_checked_out else “Available”
return f”‘{self.title}’ by {self.author} – {status}”
@classmethod
def change_library_name(cls, new_name):
old_name = cls.library_name
cls.library_name = new_name
return f”Library renamed from ‘{old_name}’ to ‘{new_name}'”
@staticmethod
def validate_isbn(isbn):
# Simple validation: ISBN should be 10 or 13 digits
return len(isbn) in (10, 13) and isbn.isdigit()
def __str__(self):
return self.get_info()
def __repr__(self):
return f”LibraryBook(‘{self.title}’, ‘{self.author}’, ‘{self.isbn}’)”
# Using the class
book = LibraryBook(“The Python Magic”, “Feloriya”, “9781234567890”)
print(book) # ‘The Python Magic’ by Feloriya – Available
success, message = book.check_out(“Ali”)
print(message) # Ali checked out ‘The Python Magic’
print(book) # ‘The Python Magic’ by Feloriya – Checked Out
success, message = book.check_out(“Sara”)
print(message) # ‘The Python Magic’ is already checked out
book.return_book()
print(book) # ‘The Python Magic’ by Feloriya – Available
print(book.checkout_history) # [‘Ali’]
print(LibraryBook.validate_isbn(“9781234567890”)) # True
| What | Convention | Example |
|---|---|---|
| Class name | PascalCase | BankAccount |
| Instance method | snake_case | get_balance() |
| Class method | snake_case | from_string() |
| Static method | snake_case | validate_email() |
| Instance attribute | snake_case | self.customer_name |
| Private (internal) attribute | _single_leading_underscore | self._balance |
| Class attribute | snake_case (or UPPER_CASE for constants) | MAX_SIZE |
- Forgetting the self parameter in method definitions
- Forgetting to use self when accessing attributes inside methods
- Leaving the class empty (use functions until you need a class)
- Creating classes that are too large (single responsibility)
- Using class attributes when you meant instance attributes (leads to shared state bugs)
- Returning self for method chaining (not always appropriate)
- Write a Student class with attributes name and grades (a list). Add a method add_grade(grade) and average().
- What is the difference between an instance method and a class method?
- When would you use a static method?
- What does the __init__ method do?
- How do you create an object from a class?
- Write a BankAccount class with deposit() and withdraw() methods.
⚡ Whisper
Writing your first class is a milestone. You move from using objects to creating them. From being a passenger to being a designer. The syntax is small: class, def __init__, self. But the implications are large. You are now modeling the world. A Car is not just data. It is a thing that can drive and repaint. A Student is not just a name. It is a person who can add grades and calculate averages. You decide what attributes matter. You decide what behaviors are possible. This is power. This is responsibility. Start small. Build simple classes. Then build classes that use other classes. Soon you will see systems where you never imagined. The blueprint is in your hands. The objects are waiting. Create them.