Define a simple class, create objects from it, and interact with those objects' attributes and methods. This hands-on exercise helps understand 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.
Was this section helpful?
© 2026 ApX Machine LearningEngineered with