Lesson 1 - Introduction to Object-Oriented Programming
Introduction
So far, we’ve been working with Python’s built-in data types like strings, lists, and dictionaries. While these are powerful, real-world applications often need custom data structures that better represent the things we’re modeling. This is where Object-Oriented Programming (OOP) comes in.
Object-oriented programming is a programming paradigm that organizes code around objects rather than just functions and logic. An object bundles together data (called attributes) and the functions that work with that data (called methods).
Think of it this way: when you’re building a bookstore application, you don’t just want a bunch of disconnected lists of book titles, prices, and authors. You want a Book object that keeps all that information together and knows how to do things like calculate discounts or display itself nicely.
In this lesson, we’ll learn:
- What classes and objects are
- How to create a class and instantiate objects
- How to define attributes and methods
- How to use the special
__init__method - What
selfmeans and why it’s important
What Are Classes and Objects?
A class is like a blueprint or template for creating objects. It defines what attributes (data) and methods (functions) the objects will have.
An object is an instance of a class—a specific example created from the blueprint.
For example:
Bookcould be a class (the blueprint)- A specific book like “Python Crash Course” would be an object (an instance of the
Bookclass)
Let’s start with a simple example:
class Book:
pass # An empty class for nowWe’ve created a class called Book. By convention, class names use PascalCase (capitalize each word). Now let’s create an object from this class:
my_book = Book()
print(my_book)Output:
<__main__.Book object at 0x7f8b1c3e4a90>We’ve created an object! The output shows it’s a Book object. Of course, this object doesn’t do much yet because our class is empty.
Adding Attributes
Let’s make our Book class more useful by adding attributes—the data that each book object will store. We’ll use the special __init__ method, which is automatically called when a new object is created.
class Book:
def __init__(self, title, author, price):
self.title = title
self.author = author
self.price = priceLet’s break this down:
__init__is a special method called a constructor. It runs automatically when you create a new object.selfrefers to the object being created. Every method in a class takesselfas its first parameter.self.title = titletakes thetitleargument and stores it as an attribute of the object.
Now we can create book objects with actual data:
book1 = Book("Python Crash Course", "Eric Matthes", 39.99)
book2 = Book("Automate the Boring Stuff", "Al Sweigart", 29.99)
print(book1.title) # Python Crash Course
print(book1.author) # Eric Matthes
print(book2.price) # 29.99Each object (book1 and book2) has its own set of attributes. Changing book1.price won’t affect book2.price.
Understanding self
The self parameter is crucial in OOP but can be confusing at first. Here’s what you need to know:
selfrepresents the specific object (instance) that’s being worked with- When you call
book1.title, Python automatically passesbook1asself - You don’t pass
selfyourself when calling methods—Python does it for you
Here’s an example to illustrate:
class Book:
def __init__(self, title, author, price):
self.title = title
self.author = author
self.price = price
def display_info(self):
print(f"'{self.title}' by {self.author}")
print(f"Price: ${self.price}")
book1 = Book("Python Crash Course", "Eric Matthes", 39.99)
book1.display_info()Output:
'Python Crash Course' by Eric Matthes
Price: $39.99When we call book1.display_info(), we don’t pass any arguments, but the method definition has self. Python automatically passes book1 as self, so inside the method, self.title refers to book1.title.
Adding Instance Methods
Methods are functions that belong to a class. They can access and modify the object’s attributes using self. Let’s add some useful methods to our Book class:
class Book:
def __init__(self, title, author, price):
self.title = title
self.author = author
self.price = price
def display_info(self):
print(f"'{self.title}' by {self.author}")
print(f"Price: ${self.price}")
def apply_discount(self, discount_percent):
discount_amount = self.price * (discount_percent / 100)
self.price = self.price - discount_amount
return self.price
def is_expensive(self):
return self.price > 30
# Create a book
book = Book("Learning Python", "Mark Lutz", 54.99)
# Use the methods
book.display_info()
print(f"Is expensive? {book.is_expensive()}")
new_price = book.apply_discount(20)
print(f"After 20% discount: ${new_price:.2f}")Output:
'Learning Python' by Mark Lutz
Price: $54.99
Is expensive? True
After 20% discount: $43.99Notice how methods can:
- Take parameters (
discount_percent) - Modify object attributes (
self.price) - Return values (
return self.price) - Access other attributes (
self.price > 30)
A Practical Example: Managing a Bookstore
Let’s build a more complete example that manages a collection of books:
class Book:
def __init__(self, title, author, price, quantity):
self.title = title
self.author = author
self.price = price
self.quantity = quantity
def display_info(self):
print(f"\n{self.title}")
print(f"Author: {self.author}")
print(f"Price: ${self.price}")
print(f"In stock: {self.quantity}")
def sell(self, amount=1):
if amount > self.quantity:
print(f"Not enough stock! Only {self.quantity} available.")
return False
else:
self.quantity -= amount
total = self.price * amount
print(f"Sold {amount} book(s) for ${total:.2f}")
return True
def restock(self, amount):
self.quantity += amount
print(f"Restocked {amount} books. New quantity: {self.quantity}")
# Create our bookstore inventory
book1 = Book("Python Crash Course", "Eric Matthes", 39.99, 5)
book2 = Book("Automate the Boring Stuff", "Al Sweigart", 29.99, 3)
book3 = Book("Learning Python", "Mark Lutz", 54.99, 2)
# Display inventory
book1.display_info()
book2.display_info()
book3.display_info()
# Simulate sales
book1.sell(2)
book1.sell(4) # This will fail - not enough stock
book1.restock(10)
book1.sell(4) # Now it will workOutput:
Python Crash Course
Author: Eric Matthes
Price: $39.99
In stock: 5
Automate the Boring Stuff
Author: Al Sweigart
Price: $29.99
In stock: 3
Learning Python
Author: Mark Lutz
Price: $54.99
In stock: 2
Sold 2 book(s) for $79.98
Not enough stock! Only 3 available.
Restocked 10 books. New quantity: 13
Sold 4 book(s) for $159.96Working with Multiple Objects
One of the powerful aspects of OOP is that each object is independent. Let’s see this in action:
class Book:
def __init__(self, title, price):
self.title = title
self.price = price
self.sales_count = 0
def record_sale(self):
self.sales_count += 1
print(f"'{self.title}' has been sold {self.sales_count} time(s)")
# Create multiple book objects
book1 = Book("Python Basics", 29.99)
book2 = Book("Advanced Python", 44.99)
# Record sales for different books
book1.record_sale()
book1.record_sale()
book1.record_sale()
book2.record_sale()
print(f"\nTotal sales for '{book1.title}': {book1.sales_count}")
print(f"Total sales for '{book2.title}': {book2.sales_count}")Output:
'Python Basics' has been sold 1 time(s)
'Python Basics' has been sold 2 time(s)
'Python Basics' has been sold 3 time(s)
'Advanced Python' has been sold 1 time(s)
Total sales for 'Python Basics': 3
Total sales for 'Advanced Python': 1Each object maintains its own sales_count, completely independent of other objects.
Default Values in __init__
You can provide default values for attributes in the __init__ method:
class Book:
def __init__(self, title, author, price=19.99, in_stock=True):
self.title = title
self.author = author
self.price = price
self.in_stock = in_stock
def display_info(self):
stock_status = "Available" if self.in_stock else "Out of stock"
print(f"{self.title} - ${self.price} ({stock_status})")
# Create books with different combinations of arguments
book1 = Book("Python Basics", "John Doe")
book2 = Book("Advanced Python", "Jane Smith", 49.99)
book3 = Book("Python Reference", "Bob Johnson", 34.99, False)
book1.display_info()
book2.display_info()
book3.display_info()Output:
Python Basics - $19.99 (Available)
Advanced Python - $49.99 (Available)
Python Reference - $34.99 (Out of stock)Exercise: Build a Simple Shopping Cart
Now let’s create a ShoppingCart class that can hold books and calculate the total price:
class Book:
def __init__(self, title, price):
self.title = title
self.price = price
class ShoppingCart:
def __init__(self):
self.books = []
def add_book(self, book):
self.books.append(book)
print(f"Added '{book.title}' to cart")
def remove_book(self, title):
for book in self.books:
if book.title == title:
self.books.remove(book)
print(f"Removed '{title}' from cart")
return True
print(f"'{title}' not found in cart")
return False
def get_total(self):
total = 0
for book in self.books:
total += book.price
return total
def display_cart(self):
if not self.books:
print("Your cart is empty")
return
print("\n--- Shopping Cart ---")
for book in self.books:
print(f" {book.title}: ${book.price}")
print(f"Total: ${self.get_total():.2f}")
print("--------------------")
# Create books
book1 = Book("Python Crash Course", 39.99)
book2 = Book("Automate the Boring Stuff", 29.99)
book3 = Book("Learning Python", 54.99)
# Create and use shopping cart
cart = ShoppingCart()
cart.add_book(book1)
cart.add_book(book2)
cart.add_book(book3)
cart.display_cart()
cart.remove_book("Automate the Boring Stuff")
cart.display_cart()Output:
Added 'Python Crash Course' to cart
Added 'Automate the Boring Stuff' to cart
Added 'Learning Python' to cart
--- Shopping Cart ---
Python Crash Course: $39.99
Automate the Boring Stuff: $29.99
Learning Python: $54.99
Total: $124.97
--------------------
Removed 'Automate the Boring Stuff' from cart
--- Shopping Cart ---
Python Crash Course: $39.99
Learning Python: $54.99
Total: $94.98
--------------------Summary
In this lesson, we learned the fundamentals of object-oriented programming:
- Classes are blueprints for creating objects
- Objects are instances of classes with their own data
- Attributes store data in objects (accessed with
self.attribute_name) - Methods are functions that belong to a class and work with object data
- The
__init__method initializes new objects selfrefers to the specific object instance being worked with
Object-oriented programming helps us organize code by grouping related data and functions together. Instead of having separate variables for book titles, authors, and prices, we can create Book objects that bundle everything together.
In the next lesson, we’ll learn about class methods, static methods, properties, and encapsulation—tools that give us even more control over how our classes work.