6.1 Classes and Objects in Python

In Python, Object-Oriented Programming (OOP) is a programming paradigm that allows you to structure code by modeling real-world entities as objects. These objects are instances of classes, which serve as blueprints for creating objects. OOP principles like encapsulation, inheritance, and polymorphism help organize and manage complex code, making it reusable, modular, and maintainable.

In this section, we’ll explore the basic concepts of classes and objects, how to create them, and how to work with them.


6.1.1 What is a Class?

A class is a blueprint for creating objects. It defines a set of attributes and methods that the objects (also called instances) created from the class will have. Classes allow you to encapsulate data and functionality together.

Example:

class Dog:
    # Class attributes (shared across all instances)
    species = "Canis lupus familiaris"

    # Initializer method (constructor)
    def __init__(self, name, age):
        self.name = name  # Instance attribute
        self.age = age    # Instance attribute

    # Method (function inside a class)
    def bark(self):
        return f"{self.name} says woof!"

In this example:

  • Dog is the class.
  • name and age are instance attributes that vary for each dog object.
  • species is a class attribute that is shared by all dogs.
  • bark() is a method that performs an action (in this case, making the dog bark).

6.1.2 What is an Object?

An object is an instance of a class. You can create as many objects as you like from a class. Each object can have its own unique attributes and behavior based on the class definition.

Example:

# Creating instances (objects) of the Dog class
dog1 = Dog("Buddy", 5)
dog2 = Dog("Bella", 3)

# Accessing instance attributes and methods
print(dog1.name)  # Output: Buddy
print(dog2.age)   # Output: 3
print(dog1.bark())  # Output: Buddy says woof!

In this example:

  • dog1 and dog2 are two objects (instances) of the Dog class.
  • Each object has its own instance attributes (name and age), but they share the class attribute species.

6.1.3 The __init__() Method (Constructor)

The __init__() method is a special method in Python known as the constructor. It is automatically called when you create a new object from a class. The __init__() method is used to initialize the attributes of the object.

Example:

class Car:
    def __init__(self, brand, model, year):
        self.brand = brand  # Instance attribute
        self.model = model  # Instance attribute
        self.year = year    # Instance attribute

    def description(self):
        return f"{self.year} {self.brand} {self.model}"

The __init__() method initializes the attributes brand, model, and year for each car object.

Creating Objects:

car1 = Car("Toyota", "Corolla", 2020)
car2 = Car("Honda", "Civic", 2019)

print(car1.description())  # Output: 2020 Toyota Corolla
print(car2.description())  # Output: 2019 Honda Civic

6.1.4 Instance Attributes vs. Class Attributes

  • Instance attributes are specific to each object (instance) of a class. They are defined in the __init__() method and are prefixed with self to indicate they belong to the instance.
  • Class attributes are shared among all objects of the class and are defined outside the __init__() method.

Example:

class Circle:
    # Class attribute
    pi = 3.14159

    def __init__(self, radius):
        # Instance attribute
        self.radius = radius

    def area(self):
        return Circle.pi * self.radius ** 2

In this example:

  • pi is a class attribute, and it is shared by all Circle objects.
  • radius is an instance attribute that is specific to each Circle object.

Using the Class Attribute:

circle1 = Circle(5)
circle2 = Circle(10)

print(circle1.area())  # Output: 78.53975
print(circle2.area())  # Output: 314.159

Here, both circle1 and circle2 share the same value of pi but have different radii, resulting in different areas.


6.1.5 Methods in Classes

Methods are functions defined inside a class that operate on the object’s attributes. The first parameter of any method is always self, which refers to the instance of the class.

Example of a Method:

class Rectangle:
    def __init__(self, length, width):
        self.length = length
        self.width = width

    def area(self):
        return self.length * self.width

    def perimeter(self):
        return 2 * (self.length + self.width)

Creating and Using Methods:

rect = Rectangle(10, 5)
print(f"Area: {rect.area()}")          # Output: Area: 50
print(f"Perimeter: {rect.perimeter()}")  # Output: Perimeter: 30

In this example:

  • The area() method calculates the area of the rectangle.
  • The perimeter() method calculates the perimeter of the rectangle.

6.1.6 The self Parameter

The self parameter is a reference to the current instance of the class. It allows you to access instance attributes and methods from within the class. Although the name self is conventional, you can name it anything, but it's important to always include it as the first parameter in instance methods.

Example:

class Person:
    def __init__(self, name):
        self.name = name  # `self.name` refers to the instance attribute

    def greet(self):
        return f"Hello, my name is {self.name}"

Here, self.name refers to the instance attribute name, which is unique to each object created from the Person class.


6.1.7 Modifying Object Attributes

You can modify an object’s attributes either inside a method or directly through the object.

Example: Modifying Inside a Method:

class BankAccount:
    def __init__(self, balance):
        self.balance = balance

    def deposit(self, amount):
        self.balance += amount

    def withdraw(self, amount):
        self.balance -= amount

Example: Modifying Directly:

account = BankAccount(1000)
print(account.balance)  # Output: 1000

account.deposit(500)
print(account.balance)  # Output: 1500

account.withdraw(200)
print(account.balance)  # Output: 1300

In this example:

  • The deposit() and withdraw() methods modify the balance attribute of the BankAccount object.
  • You can also directly modify the balance by accessing the attribute from the object.

6.1.8 Deleting Object Attributes

You can delete an object's attribute using the del keyword.

Example:

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author

book = Book("1984", "George Orwell")
print(book.title)  # Output: 1984

del book.title  # Deletes the 'title' attribute

# print(book.title)  # Raises AttributeError, as the attribute no longer exists

In this example, the title attribute is deleted from the book object, and attempting to access it afterward raises an AttributeError.


6.1.9 Summary

  • A class is a blueprint for creating objects. It defines the attributes and methods that the objects (instances) created from the class will have.
  • An object is an instance of a class, created using the class definition.
  • The __init__() method is the constructor that initializes the object’s attributes when it is created.
  • Instance attributes are specific to each object, while class attributes are shared by all objects of the class.
  • Methods are functions defined inside a class that operate on the object’s attributes. They always have self as the first parameter, referring to the instance.
  • The self parameter allows methods to access and modify instance attributes.
  • Object attributes can be modified inside methods or directly, and they can be deleted using the del keyword.

Understanding classes and objects is fundamental to writing modular, reusable, and maintainable code in Python. Mastering object-oriented programming will help you structure complex applications more effectively.