Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
115: Classes and Objects: The Basics
I've seen this exact snippet of code dozens of times when mentoring junior devs. It looks perfectly logical at first glance, but the moment you run it, Python throws an error that seems to make no sense.
class User:
def __init__(self, username, email):
self.username = username
self.email = email
def welcome_message():
print(f"Welcome back, {self.username}!")
user1 = User("dev_dan", "dan@example.com")
user1.welcome_message()
If you run this, you'll get a TypeError: welcome_message() takes 0 positional arguments but 1 was given. You're probably looking at the call user1.welcome_message() and thinking, "I didn't give it any arguments! The parentheses are empty!"
The Hidden Argument Problem
Here is what's happening behind the scenes: in Python, when you call a method on an object, Python automatically passes the object itself as the first argument. So, user1.welcome_message() is actually executed as User.welcome_message(user1).
Because our definition of welcome_message() doesn't accept any arguments, Python complains that it received one (the user1 object) when it expected zero. This is why you see self everywhere in Python classes. It isn't a keyword, but it is a very strong convention. It's our way of saying, "This method needs to know which specific object it's working with."
Fixing the Method Signature
To fix this, we just need to tell the method to expect that object. We do this by adding self as the first parameter.
class User:
def __init__(self, username, email):
self.username = username
self.email = email
# Added 'self' here
def welcome_message(self):
print(f"Welcome back, {self.username}!")
user1 = User("dev_dan", "dan@example.com")
user1.welcome_message() # Now this works!
Blueprints versus Actual Houses
To wrap your head around classes and objects, I find it easiest to think of a Class as a blueprint. If you have a blueprint for a house, you can't live in the blueprint. It's just a piece of paper that describes where the walls go and where the plumbing is. It defines the structure.
An Object (or an Instance) is the actual house built from that blueprint. You can build ten different houses from one blueprint. They all have the same structure, but one might be painted blue, one might be red, and one might have a different family living inside. In Python terms, the "paint color" is what we call an instance variable.
The Role of __init__
You'll notice the __init__ method in the examples above. I call this the "setup" method. It's a special method that Python runs automatically the moment you create a new object. Its primary job is to assign the initial state to the object.
When we write self.username = username, we are taking the value passed in during creation and "sticking" it to the object. Without that self. prefix, the variable would just be a local variable inside the function and would disappear as soon as __init__ finished running.
Grouping Data and Behavior
The real power of this approach is that we stop having disconnected pieces of data. Instead of having a list of usernames and a separate list of emails, we have a User object that carries its own data and its own functions (methods) to manipulate that data.
- Class: The blueprint (e.g.,
User). - Object: The instance (e.g.,
user1). - Attribute: A variable tied to the object (e.g.,
self.email). - Method: A function tied to the object (e.g.,
welcome_message()).
📋 Practical Task
Building a Digital Library Catalog
You need to create a system to track books in a library. Your task is to build a Book class that manages the status of a book.
Requirements:
- Create a class named
Book. - The
__init__method should take three parameters:title,author, andisbn. - The
__init__method should also create an attribute calledis_checked_outand set it toFalseby default. - Add a method called
borrow_book(). If the book is not checked out, it should setis_checked_outtoTrueand print "You have borrowed [title]". If it is already checked out, it should print "Sorry, [title] is already taken". - Add a method called
return_book(). It should setis_checked_outtoFalseand print "You have returned [title]".
Test your code by:
- Creating an instance of a book (e.g., "The Hobbit" by J.R.R. Tolkien).
- Calling
borrow_book()once to check it out. - Calling
borrow_book()a second time to ensure it tells you the book is already taken. - Calling
return_book()and then borrowing it successfully again.
There are no comments for now.