4.6 Classes

Object-oriented programming (OOP) is a programming paradigm based on the concept of “objects”, which can contain data and code. The data is in the form of fields (often known as attributes or properties), and the code is in the form of procedures (often known as methods).

Generating an object from a class is called instantiation. You work with instances of a class, which are created by the process of instantiating an object from a class.

Using functions

class Student:
    """
    A class is used to represent a student.

    ...

    Attributes
    ----------
    name: str
        the name of the students
    age: int
        the age of the students

    Methods
    -------
    learn()
        Simulate a student's learning.
    take_exam()
        Simulate taking over good results.
    """

    def __init__(self, name, age):
        """Initialize name and age attributes.

        Attributes
        ----------
        name: str
            the name of the students
        age: int
            the age of the students

        Returns:
            None
        """
        self.name = name
        self.age = age

    def learn(self):
        """Simulate a student's learning.

        Prints status

        Attributes
        ----------
        name: str
            the name of the students
        age: int
            the age of the students

        Returns:
            None
        """
        print(f"{self.name} is now learning.")

    def take_exam(self):
        """Simulate taking over good results.

        Prints encouragement

        Attributes
        ----------
        name: str
            the name of the students
        age: int
            the age of the students

        Returns:
            None
        """
        print(f"{self.name} did great!")

print(f"Student1 is {student1.name} and is {student1.age}")
print(f"Student2 is {student2.name} and is {student2.age}")

student1 = Student("John Wick", 47)
student2 = Student("Macaulay Culkin", 12)

A method is a function that is a part of a class. Everything you learned about functions also applies to methods; the only difference in practice is that we’ll refer to methods as methods.

__init__ is a special method called a constructor that Python calls when you create a new instance of this class.

Other special methods include __str__, __eq__, and __len__. You can override these methods to customize how they work with your objects.

Buzzwords of OOP

  • Class: A blueprint for how something should be defined. It doesn’t actually contain any data.

  • Object: An instance of a class. It contains the actual data.

  • Attribute: A bit of data inside a class or object.

  • Method: An action that a class or object can take.

  • Inheritance: The ability to extend a class to make a new class.

  • Composition: The ability to call functions from another class, or build a bigger class from smaller classes.

  • Encapsulation: A way to make sure that data can only be modified in certain ways.

  • Polymorphism: A way to make different classes interchangeable with each other.

  • Class variable: A variable that’s the same for every instance of a class.

  • Instance variable: A variable that’s unique to each instance of a class.

  • Constructor: The method that runs when an object is instantiated from a class.

What is Inheritance?

Inheritance is the process by which one class takes on the attributes and methods of another. Newly formed classes are called child classes, and the classes that child classes are derived from are called parent classes.

# Example on inheritance in Python
# Parent class
class Animal:
    def __init__(self, name, weight):
        self.name = name
        self.weight = weight

    def eat(self):
        print(f"{self.name} is eating.")

    def drink(self):
        print(f"{self.name} is drinking.")

# Child class
class Frog(Animal):
    def jump(self):
        print(f"{self.name} is jumping.")

# Child class
class Dog(Animal):
    def bark(self):
        print(f"{self.name} is barking.")

frog = Frog("Kermit", 0.5)
dog = Dog("Scooby", 10)

frog.eat()
frog.drink()
frog.jump()

dog.eat()
dog.drink()
dog.bark()

What is Composition?

Composition is a way of building up larger classes from smaller ones. It refers to the way a class is made up of other classes as opposed to inheritance, which refers to the way a class is derived from another class.

# Example on composition in Python
class Book:
    def __init__(self, title, price, author=None):
        self.title = title
        self.price = price

        self.author = author

        self.chapters = []

    def add_chapter(self, chapter):
        self.chapters.append(chapter)

    def get_book_page_count(self):
        result = 0
        for ch in self.chapters:
            result += ch.page_count
        return result

class Author:
    def __init__(self, fname, lname):
        self.fname = fname
        self.lname = lname

    def __str__(self):
        return f"{self.fname} {self.lname}"

class Chapter:
    def __init__(self, name, page_count):
        self.name = name
        self.page_count = page_count

author = Author("Leo", "Tolstoy")
b1 = Book("War and Peace", 39.0, author)

What is Encapsulation?

Encapsulation is the process of restricting access to methods and variables in a class in order to prevent direct data modification so it prevents accidental modification of data.

# Example on encapsulation in Python
class Person:

def __init__(self, name, age):
    self._name = name
    self._age = age

def get_name(self):
    return self._name

def set_name(self, name):
    self._name = name

def get_age(self):
    return self._age

def set_age(self, age):
    if age < 0:
        raise ValueError("Age cannot be negative")
    self._age = age

p = Person("John", 25)
print(p.get_name()) # Prints "John"

p.set_name("Jane")
print(p.get_name()) # Prints "Jane"
print(p.get_age()) # Prints 25

p.set_age(27)
print(p.get_age()) # Prints 27

p.set_age(-27) # Raises ValueError

What is Polymorphism?

Polymorphism in Python refers to the ability to use the same syntax for different types of objects. There are two main ways polymorphism is achieved in Python:

  1. Inheritance and overriding - A parent class can be inherited by multiple child classes, each of which can override the parent class methods.

# Example on polymorphism in Python
class Shape:
def area(self):
    pass

class Square(Shape):
def area(self):
    # calculate area for square
    pass

class Circle(Shape):
def area(self):
    # calculate area for circle
    pass
# Here Square and Circle inherit from Shape, but override the area method to provide their own implementation.
  1. Duck typing - Different unrelated objects can have the same method names, allowing them to be used interchangeably.

class Car:
def drive(self):
    print("Driving the car")

class Bike:
def drive(self):
    print("Driving the bike")

# `vehicle` can refer to either a Car or Bike object
vehicle.drive()
# As long as objects have a drive method, they can be treated the same way despite having different underlying types.