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.Defining the 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.Initializing Objects with __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 objectInside __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.Adding Behavior with MethodsObjects 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).Creating and Using 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.Accessing Attributes and Calling MethodsOnce 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.Complete Example CodeHere'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.