Object-Oriented Programming in Python

Welcome to our lesson on Object-Oriented Programming (OOP) in Python! As JavaScript developers, you’re already familiar with OOP concepts. In this lesson, we’ll explore how Python implements these ideas, highlighting the similarities and differences you’ll encounter.

Class Definition and Instantiation

In Python, class definitions have a structure that might feel both familiar and different:

class Car:
    def __init__(self, make, model):
        self.make = make
        self.model = model

    def describe(self):
        return f"This is a {self.make} {self.model}."

my_car = Car("Toyota", "Corolla")
print(my_car.describe())  # Output: This is a Toyota Corolla.

In JavaScript, you might write:

class Car {
    constructor(make, model) {
        this.make = make;
        this.model = model;
    }

    describe() {
        return `This is a ${this.make} ${this.model}.`;
    }
}

const myCar = new Car("Toyota", "Corolla");
console.log(myCar.describe());  // Output: This is a Toyota Corolla.

Key differences:

  • Python uses def for methods, including the constructor (__init__).
  • The self parameter in Python is equivalent to this in JavaScript.
  • Python doesn’t use the new keyword for instantiation.

Methods and the self Parameter

In Python, instance methods always take self as their first parameter:

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def area(self):
        return self.width * self.height

rect = Rectangle(5, 3)
print(rect.area())  # Output: 15

This is different from JavaScript, where this is implicit:

class Rectangle {
    constructor(width, height) {
        this.width = width;
        this.height = height;
    }
    
    area() {
        return this.width * this.height;
    }
}

const rect = new Rectangle(5, 3);
console.log(rect.area());  // Output: 15

Inheritance and Method Overriding

Python supports inheritance with a syntax that’s quite straightforward:

class Vehicle:
    def __init__(self, wheels):
        self.wheels = wheels

    def describe(self):
        return f"This vehicle has {self.wheels} wheels."

class Car(Vehicle):
    def __init__(self, make, model):
        super().__init__(4)
        self.make = make
        self.model = model

    def describe(self):
        return f"{super().describe()} It's a {self.make} {self.model}."

my_car = Car("Honda", "Civic")
print(my_car.describe())  # Output: This vehicle has 4 wheels. It's a Honda Civic.

In JavaScript, you might write:

class Vehicle {
    constructor(wheels) {
        this.wheels = wheels;
    }

    describe() {
        return `This vehicle has ${this.wheels} wheels.`;
    }
}

class Car extends Vehicle {
    constructor(make, model) {
        super(4);
        this.make = make;
        this.model = model;
    }

    describe() {
        return `${super.describe()} It's a ${this.make} ${this.model}.`;
    }
}

const myCar = new Car("Honda", "Civic");
console.log(myCar.describe());  // Output: This vehicle has 4 wheels. It's a Honda Civic.

Key points:

  • Python uses parentheses for inheritance: class Car(Vehicle):.
  • Both languages use super() to call the parent class’s methods.

Multiple Inheritance

One feature Python offers that JavaScript doesn’t is multiple inheritance:

class Flyable:
    def fly(self):
        return "I can fly!"

class Swimmable:
    def swim(self):
        return "I can swim!"

class FlyingFish(Flyable, Swimmable):
    pass

fish = FlyingFish()
print(fish.fly())   # Output: I can fly!
print(fish.swim())  # Output: I can swim!

JavaScript doesn’t support multiple inheritance, but you can achieve similar functionality using mixins or composition.

Encapsulation and Data Privacy

Python doesn’t have built-in private variables, but it uses conventions:

class BankAccount:
    def __init__(self, balance):
        self._balance = balance  # Convention: '_' prefix for "private" attributes

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

    def get_balance(self):
        return self._balance

account = BankAccount(1000)
account.deposit(500)
print(account.get_balance())  # Output: 1500
print(account._balance)  # This works, but it's considered bad practice

In JavaScript, you might use the # prefix for truly private fields (a more recent feature):

class BankAccount {
    #balance;

    constructor(balance) {
        this.#balance = balance;
    }

    deposit(amount) {
        this.#balance += amount;
    }

    getBalance() {
        return this.#balance;
    }
}

const account = new BankAccount(1000);
account.deposit(500);
console.log(account.getBalance());  // Output: 1500
// console.log(account.#balance);  // This would cause an error

Python relies more on convention and developer discipline for encapsulation, while JavaScript offers stronger enforcement of privacy.

Conclusion

In this lesson, we’ve explored the key aspects of Object-Oriented Programming in Python, drawing comparisons with JavaScript. We’ve seen how Python implements classes, inheritance, and encapsulation, noting both the similarities and the unique features Python offers.

As you continue your Python journey, remember that while the underlying OOP concepts are similar, Python’s implementation often reflects its philosophy of simplicity and readability. Practice creating and working with classes in Python, and you’ll soon find yourself thinking in “Pythonic” OOP patterns!

In our next lesson, we’ll dive into Python’s module and package system, exploring how Python organizes and reuses code compared to JavaScript’s module system. Get ready to discover how Python’s import statements and package structure can help you build well-organized, maintainable projects!