Classes are blueprints for creating objects. They define attributes (data) and methods (functions) that objects will have.
# Basic class definition
class Person:
def __init__(self, name, age):
self.name = name # Instance attribute
self.age = age
def introduce(self): # Instance method
return f"Hi, I'm {name}, {age} years old"
# Creating objects (instances)
person1 = Person("Alice", 25)
person2 = Person("Bob", 30)
print(person1.name) # Alice
print(person1.introduce()) # Hi, I'm Alice, 25 years old
class Student:
# Class attribute (shared by all instances)
school_name = "Python High School"
total_students = 0
def __init__(self, name, grade):
# Instance attributes (unique to each instance)
self.name = name
self.grade = grade
Student.total_students += 1 # Increment class attribute
def study(self):
return f"{self.name} is studying"
@classmethod
def get_school_info(cls):
return f"School: {cls.school_name}, Students: {cls.total_students}"
# Creating instances
student1 = Student("Alice", "A")
student2 = Student("Bob", "B")
print(Student.school_name) # Python High School
print(student1.school_name) # Python High School (inherited)
print(Student.get_school_info()) # School: Python High School, Students: 2
# Base class (parent)
class Animal:
def __init__(self, name, species):
self.name = name
self.species = species
def make_sound(self):
return "Some generic animal sound"
def info(self):
return f"{self.name} is a {self.species}"
# Derived class (child)
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name, "Dog") # Call parent constructor
self.breed = breed
def make_sound(self): # Override parent method
return "Woof!"
def fetch(self): # New method specific to Dog
return f"{self.name} is fetching the ball"
class Cat(Animal):
def __init__(self, name, color):
super().__init__(name, "Cat")
self.color = color
def make_sound(self):
return "Meow!"
def climb(self):
return f"{self.name} is climbing a tree"
# Using inheritance
dog = Dog("Buddy", "Golden Retriever")
cat = Cat("Whiskers", "Orange")
print(dog.info()) # Buddy is a Dog
print(dog.make_sound()) # Woof!
print(dog.fetch()) # Buddy is fetching the ball
print(cat.info()) # Whiskers is a Cat
print(cat.make_sound()) # Meow!
class BankAccount:
def __init__(self, account_number, initial_balance=0):
self.account_number = account_number
self._balance = initial_balance # Protected attribute (convention)
self.__pin = "1234" # Private attribute (name mangling)
def deposit(self, amount):
if amount > 0:
self._balance += amount
return f"Deposited ${amount}. New balance: ${self._balance}"
return "Invalid deposit amount"
def withdraw(self, amount, pin):
if not self.__verify_pin(pin):
return "Invalid PIN"
if amount > self._balance:
return "Insufficient funds"
self._balance -= amount
return f"Withdrew ${amount}. New balance: ${self._balance}"
def get_balance(self, pin):
if self.__verify_pin(pin):
return f"Current balance: ${self._balance}"
return "Invalid PIN"
def __verify_pin(self, pin): # Private method
return pin == self.__pin
# Using the class
account = BankAccount("12345", 1000)
print(account.deposit(500)) # Deposited $500. New balance: $1500
print(account.withdraw(200, "1234")) # Withdrew $200. New balance: $1300
print(account.get_balance("1234")) # Current balance: $1300
# These would not work as expected:
# print(account.__pin) # AttributeError
# print(account.__verify_pin("1234")) # AttributeError
# Polymorphism allows objects of different classes to be treated uniformly
class Shape:
def area(self):
pass
def perimeter(self):
pass
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
def perimeter(self):
return 2 * 3.14159 * self.radius
# Polymorphism in action
shapes = [
Rectangle(5, 3),
Circle(4),
Rectangle(2, 8)
]
for shape in shapes:
print(f"Area: {shape.area():.2f}, Perimeter: {shape.perimeter():.2f}")
# Each shape calculates area and perimeter differently
class Book:
def __init__(self, title, author, pages):
self.title = title
self.author = author
self.pages = pages
def __str__(self): # String representation for users
return f"'{self.title}' by {self.author}"
def __repr__(self): # String representation for developers
return f"Book('{self.title}', '{self.author}', {self.pages})"
def __len__(self): # Makes len() work
return self.pages
def __eq__(self, other): # Makes == work
if isinstance(other, Book):
return self.title == other.title and self.author == other.author
return False
def __lt__(self, other): # Makes < work (for sorting)
return self.pages < other.pages
# Using special methods
book1 = Book("1984", "George Orwell", 328)
book2 = Book("Animal Farm", "George Orwell", 112)
print(str(book1)) # '1984' by George Orwell
print(repr(book1)) # Book('1984', 'George Orwell', 328)
print(len(book1)) # 328
print(book1 == book2) # False
print(book1 > book2) # True (328 > 112 pages)
books = [book1, book2]
books.sort() # Uses __lt__ method
print([str(book) for book in books]) # Sorted by pages
class Temperature:
def __init__(self, celsius=0):
self._celsius = celsius
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if value < -273.15:
raise ValueError("Temperature cannot be below absolute zero")
self._celsius = value
@property
def fahrenheit(self):
return (self._celsius * 9/5) + 32
@fahrenheit.setter
def fahrenheit(self, value):
self.celsius = (value - 32) * 5/9
@property
def kelvin(self):
return self._celsius + 273.15
# Using properties
temp = Temperature(25)
print(f"Celsius: {temp.celsius}") # 25
print(f"Fahrenheit: {temp.fahrenheit}") # 77.0
print(f"Kelvin: {temp.kelvin}") # 298.15
temp.fahrenheit = 100 # Sets celsius automatically
print(f"Celsius: {temp.celsius}") # 37.77...