Python - Classes and Objects

Hello there, aspiring Python programmers! Today, we're going to embark on an exciting journey into the world of Object-Oriented Programming (OOP) in Python. Buckle up, because we're about to transform the way you think about code!

Python - Classes & Objects

What is a Class in Python?

Imagine you're building a virtual zoo. You wouldn't create each animal from scratch, right? That's where classes come in handy!

A class in Python is like a blueprint or a template for creating objects. It defines a set of attributes and methods that the objects of that class will have.

Let's create a simple class to represent a dog:

class Dog:
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed

    def bark(self):
        print(f"{self.name} says Woof!")

In this example, Dog is our class. It has attributes like name and breed, and a method called bark().

Creating Classes in Python

Creating a class is as simple as using the class keyword followed by the class name. Let's create a more detailed Car class:

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

    def drive(self, distance):
        self.odometer += distance
        print(f"Drove {distance} miles. Total: {self.odometer} miles")

    def honk(self):
        print("Beep beep!")

Here, __init__ is a special method called a constructor. It's called when we create a new object of this class.

What is an Object?

If a class is a blueprint, an object is the actual "thing" created from that blueprint. It's a specific instance of a class.

Creating Objects of Classes in Python

Creating an object (also called instantiating) is like using our blueprint to create something real. Let's create some cars:

my_car = Car("Toyota", "Corolla", 2020)
your_car = Car("Honda", "Civic", 2019)

print(my_car.make)  # Output: Toyota
your_car.drive(50)  # Output: Drove 50 miles. Total: 50 miles
my_car.honk()  # Output: Beep beep!

Each car is a separate object with its own set of attributes and methods.

Accessing Attributes of Objects in Python

We can access the attributes of an object using dot notation. Let's examine our car:

print(f"My car is a {my_car.year} {my_car.make} {my_car.model}")
# Output: My car is a 2020 Toyota Corolla

Built-In Class Attributes in Python

Python classes come with some built-in attributes. Let's explore them:

class MyClass:
    x = 5

print(MyClass.__name__)  # Output: MyClass
print(MyClass.__module__)  # Output: __main__
print(MyClass.__dict__)  # Output: {'__module__': '__main__', 'x': 5, ...}

Built-in Class of Python datatypes

Did you know that even basic Python types are objects? Let's check it out:

print(type(5))  # Output: <class 'int'>
print(type("Hello"))  # Output: <class 'str'>
print(type([1, 2, 3]))  # Output: <class 'list'>

Everything in Python is an object!

Garbage Collection (Destroying Objects) in Python

In Python, we don't need to manually destroy objects. Python's garbage collector automatically takes care of it when an object is no longer in use.

import gc

# Create an object
my_car = Car("Toyota", "Corolla", 2020)

# Remove the reference
my_car = None

# Force garbage collection (usually not necessary)
gc.collect()

Data Hiding in Python

In OOP, we often want to control access to certain attributes. Python uses a convention of prefixing attributes with underscores:

class BankAccount:
    def __init__(self, balance):
        self._balance = balance  # Protected attribute
        self.__secret = "shh"  # Private attribute

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

    def get_balance(self):
        return self._balance

account = BankAccount(1000)
print(account._balance)  # Output: 1000 (but we shouldn't do this!)
print(account.__secret)  # This will raise an AttributeError

Here's a table summarizing the methods we've covered:

Method Description
__init__(self, ...) Constructor method, called when creating a new object
__str__(self) Returns a string representation of the object
__repr__(self) Returns a detailed string representation of the object
__len__(self) Defines behavior for the built-in len() function
__getitem__(self, key) Defines behavior for indexing operations
__setitem__(self, key, value) Defines behavior for assigning to indexed values

And there you have it! You've just taken your first steps into the world of Object-Oriented Programming in Python. Remember, practice makes perfect. Try creating your own classes and objects, and soon you'll be building complex, efficient programs like a pro!

Credits: Image by storyset