Okay, let's put the concepts from this chapter into practice. We'll define a simple class, create objects from it, and interact with those objects' attributes and methods. This hands-on exercise will solidify your understanding of how classes serve as blueprints and objects are the actual things built from those blueprints.
Imagine we want to represent dogs in our program. Each dog might have a name and a breed. Dogs can also perform actions, like barking. Let's model this using a class.
Dog
ClassWe start by using the class
keyword, followed by the name we want to give our class (conventionally, class names start with a capital letter).
class Dog:
pass # 'pass' is a placeholder, meaning 'do nothing for now'
This is the simplest possible class definition. It doesn't do much yet, but it's a valid starting point.
__init__
To make our Dog
objects useful, they need data like a name and breed. We use the special __init__
method, often called a constructor, to set up the initial state of an object when it's created.
Remember, the first parameter of any method inside a class is conventionally named self
. It refers to the specific instance (object) being created or worked on.
class Dog:
# The __init__ method initializes a new Dog object
def __init__(self, name, breed):
# 'self' refers to the instance being created
# We store the passed-in name and breed as attributes of this instance
self.name = name
self.breed = breed
print(f"Dog object named '{self.name}' created!")
# Now, __init__ automatically runs when we create a Dog object
Inside __init__
, we take name
and breed
as arguments (in addition to self
). We then assign these values to attributes of the object using self.attribute_name = value
. So, self.name = name
creates an attribute named name
on the specific Dog
object being created and assigns the provided name
value to it. Similarly, self.breed = breed
stores the breed.
Objects don't just hold data; they can also perform actions. We define these actions as methods within the class. Methods are functions defined inside a class. Let's add a bark
method.
class Dog:
def __init__(self, name, breed):
self.name = name
self.breed = breed
print(f"Dog object named '{self.name}' created!")
# A method defining an action the Dog can perform
def bark(self):
# Methods also take 'self' as the first parameter
# This allows the method to access the object's attributes
print(f"{self.name} says: Woof!")
Notice that the bark
method also takes self
as its first parameter. This is essential because it allows the method to access the object's own data, like its name
attribute (self.name
).
Dog
Objects (Instances)Now that we have our Dog
blueprint (the class), we can create actual Dog
objects (instances). Creating an instance looks like calling the class name as if it were a function, passing the arguments required by the __init__
method (excluding self
, which Python handles automatically).
# Create the first Dog instance
dog1 = Dog("Buddy", "Golden Retriever")
# Create a second Dog instance
dog2 = Dog("Lucy", "Poodle")
When you run this code, the __init__
method is called for each object creation, and you'll see the "Dog object created!" messages print. We now have two distinct Dog
objects, stored in the variables dog1
and dog2
.
Once you have an object, you can access its attributes using dot notation (object.attribute_name
) and call its methods using dot notation followed by parentheses (object.method_name()
).
# Access attributes of dog1
print(f"{dog1.name} is a {dog1.breed}.") # Output: Buddy is a Golden Retriever.
# Access attributes of dog2
print(f"{dog2.name} is a {dog2.breed}.") # Output: Lucy is a Poodle.
# Call the bark method on dog1
dog1.bark() # Output: Buddy says: Woof!
# Call the bark method on dog2
dog2.bark() # Output: Lucy says: Woof!
Notice how dog1.bark()
prints Buddy's name, and dog2.bark()
prints Lucy's name. Even though they share the same bark
method definition from the class, the method operates on the specific object it's called on, thanks to the self
parameter.
Here's the full code for our simple Dog
class and its usage:
# Define the Dog class
class Dog:
# Constructor to initialize attributes
def __init__(self, name, breed):
self.name = name # Assign the name attribute
self.breed = breed # Assign the breed attribute
print(f"Dog object named '{self.name}' of breed '{self.breed}' created.")
# Method for the dog's behavior
def bark(self):
print(f"{self.name} says: Woof!")
# Another example method
def describe(self):
print(f"This dog is named {self.name} and is a {self.breed}.")
# --- Using the Class ---
# Create instances (objects) of the Dog class
my_dog = Dog("Rex", "German Shepherd")
your_dog = Dog("Daisy", "Beagle")
print("\n--- Accessing Attributes ---")
# Access object attributes using dot notation
print(f"My dog's name: {my_dog.name}")
print(f"Your dog's breed: {your_dog.breed}")
print("\n--- Calling Methods ---")
# Call object methods using dot notation
my_dog.bark()
your_dog.bark()
my_dog.describe()
your_dog.describe()
Run this code yourself. Experiment by adding more attributes (like age
) to the __init__
method or creating more methods (like wag_tail()
). This hands-on practice is fundamental to understanding how OOP helps organize code by grouping data (attributes) and behavior (methods) together into logical units called objects.
© 2025 ApX Machine Learning