The Basics of Python Classes and Objects

Introduction

Object-oriented programming (OOP), which is a programming paradigm that groups code and the data it uses together, is a key feature in Python. The topic of OOP concepts and techniques alone can be covered by a full course in its own right. Since this course is more focused on Python features, this lesson will only cover the basics of how OOP is done in Python, specifically focusing on Python classes and objects. The primary goal of this lesson is to educate you enough about Python’s OOP features to enable you to better understand the rest of this course.

Understanding the Basics

At the heart of OOP are objects and classes, which allow for the creation of modular and scalable programs.

What is an Object?

In programming, an object is a collection of data and methods that operate on that data.

Methods are functions, which are reusable blocks of code, that belong to an object and have easy access to its data.

For example, a Python list, which is an ordered collection of elements written like [1, 2, 3, 4, 5], is an object. The data of the list are the elements inside it, and it has various methods to manipulate these elements.

Example:

my_list = [1, 2, 3, 4, 5]

my_list.append(6)
print(my_list) 

my_list.remove(3)
print(my_list)

Output:

[1, 2, 3, 4, 5, 6]
[1, 2, 4, 5, 6]

In the example above:

  • my_list is a list that’s initialized to [1, 2, 3, 4, 5] (line 1).
  • The append() method is used to add the number 6 to the end of the list, resulting in [1, 2, 3, 4, 5, 6] (line 3).
  • The remove() method is then used to remove the number 3 from the list, resulting in [1, 2, 4, 5, 6] (line 6).

These methods demonstrate how objects in Python can encapsulate data and provide functionality to manipulate that data.

It is important to understand the basics of OOP when programming in Python because everything in Python is an object. It is one of the core principles of Python’s design.

What is a Class?

A class is a blueprint for creating objects. It defines a set of attributes (data) and methods that the objects will have.

For example, Python lists, such as [1, 2, 4, 5, 6], are instances of a class named list.

Objects are also referred to as instances of a class.

Real-world Analogy

Understanding classes and objects can be easier with a real-world analogy. Think of a class as a blueprint for a house. The blueprint defines the structure and features of the house, such as the number of rooms, the type of windows, and the layout.

In this analogy,

  • Class (Blueprint): A class is like a blueprint that defines the attributes (rooms, windows) and methods (functions, such as opening a window or locking a door) of a house.
  • Object (House): An object is like a specific house built from the blueprint. Each house built from the same blueprint will have the same structure and features, but they can have different details and states, such as colors and whether a window is open or closed.

Creating Classes and Objects

Defining a Class

Now that you understand what classes and objects are, let’s look at how you can define your own a class in Python.

Here is the basic syntax of defining a class in Python:

class ClassName:
    ... # The code that defines the class.
  • class: The keyword that starts the statement that defines a new class.
  • ClassName: The name of the class. By convention, you should name classes you create with CamelCase, i.e. a text style that has the first letter of each word capitalized and the rest lower case.

In Python, ... is a special object of the ellipsis class that can be used as a placeholder for code that hasn’t been implemented yet. It is a singleton object, meaning it’s the only object of its class.

Example:

class Dog:
   pass

The example above creates a class named Dog. For now, the body of the statement is pass, which is Python’s way of having a code block that does nothing. (You will learn what you can do with this code block a little later.)

pass is a Python keyword that means no-operation. It tells Python to do nothing. Like the ellipsis, it’s another way to have a placeholder for code that you are going to fill in later.

Creating an Object

Now that you’ve defined a class, you can create objects of that class. To do so, simply write the class name followed by parentheses ().

Here is the basic syntax for creating an object:

ClassName()

Example:

my_dog = Dog()

In the example above, my_dog is a variable that’s assigned a newly created object of the Dog class.

Class Components

Instance Attributes

Instance attributes are variables that hold data specific to an instance of a class, i.e. object. They are defined within a class, but their values are unique to each object created from the class.

To define instance attributes in a class, you use the __init__() method. This is a special method that initializes an object’s attributes when the object is created. The __init__() method is called automatically by Python when a new instance of the class is created.

Here is the basic syntax for defining instance attributes within the __init__() method:

class ClassName:
    def __init__(self, attribute1, attribute2, ...):
        self.attribute1 = attribute1
        self.attribute2 = attribute2
        ...
  • def __init__(self, attribute1, attribute2, ...): Defines the __init__() method. The self parameter refers to the instance being created and allows access to its attributes and methods.
  • self.attribute1 = attribute1: Creates an instance attribute named attribute1 and assigns it the value passed to __init__() in the attribute1 parameter. The attribute and parameter don’t need to have the same name, but it usually makes the code easier to understand.

Example:

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

In the example above:

  • The Dog class has an __init__() method defined with parameters name and age (line 2).
  • The instance attributes self.name and self.age are initialized with the values passed to the __init__() method (line 3 and 4).

To pass values to the __init__() method, place them in the parentheses, separated by commas, when you create an object. The self parameter is handled by Python automatically, and you therefore don’t need to include it.

Example:

my_dog = Dog("Snoopy", 5)
print(my_dog.name)
print(my_dog.age)

Output:

Snoopy
5

In the example above:

  • my_dog is an instance of the Dog class with the name attribute set to “Snoopy” and the age attribute set to 5 (line 1).
  • The print statements output the values of the name and age attributes of my_dog (lines 2-3).

As you can see from the example, to access an object’s attribute, you follow the object by a period (.) and the attribute name. This syntax is commonly referred to as “dot notation”.

Instance attributes let each object maintain its own state and data. As the next example shows, you can create multiple Dog objects, each with a different name and age.

Example:

dog1 = Dog("Snoopy", 5)
dog2 = Dog("Scooby Doo", 3)

print(dog1.name)
print(dog2.name)

print(dog1.age)
print(dog2.age)

Output:

Snoopy
Scooby Doo
5
3

In the example above, dog1 and dog2 are two different instances of the Dog class, each with its own name and age attribute. This demonstrates that instance attributes allow objects to have unique data.

Instance Methods

Instance methods are functions that are associated with an instance of a class (a.k.a object). They can access and modify the object’s attributes and perform operations using the object’s data.

In programming, a function is a reusable block of code designed to perform a specific task. Functions will be discussed in depth in the Functions chapter of this course.

Here is the basic syntax for defining an instance method within a class:

class ClassName:
    def instance_method_name(self, param1, param2, ...):
        ... # Method body with its code
  • def instance_method_name(self, param1, param2, ...): Defines an instance method. The self parameter is mandatory and must be the first parameter in the method definition. Other than self, the method can have zero or more other parameters, denoted as param1, param2, ... here. Parameters provide data that can be used in the method’s code.
  • self: The self parameter holds the method’s object itself. Using self you can access other attributes and methods of the object with the dot notation, e.g. self.attribute_name. You can name the self parameter with any valid identifier and then use that name to reference the object in your code. However, by convention it’s always “self". Using a name other than self might be confusing to anyone else looking at your code.

Instance methods are defined in a way similar to functions, but they always have at least one parameter: self. The self parameter represents the object on which the method is called.

Example:

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def bark(self):
        return f"{self.name} says woof!"

    def birthday(self):
        self.age += 1

    def get_treats(self, number_of_treats):
        return f"{self.name} got {number_of_treats} treats."

In the example above:

  • The Dog class has two new instance methods (in addition to __init__()): bark() and birthday().
  • The bark() method (line 6) returns a string with the dog’s name and a bark sound.
  • The birthday() method (line 9) increments the dog’s age by 1.
  • The get_treats() method (line 12) returns a string acknowledging the dog received the specified number of treats. This method also demonstrates the use of a parameters in a method (number_of_treats).

To call an instance method, use dot notation on an object and place any arguments (i.e. the passed values) between the parentheses. Just don’t place a value for self because Python does it implicitly for you.

Example:

my_dog = Dog("Snoopy", 5)

print(my_dog.bark())

print(my_dog.get_treats(3))

my_dog.birthday()
print(my_dog.age)

Output:

Snoopy says woof!
Snoopy got 3 treats.
6

In the example above:

  • A Dog object is assigned to my_dog. It has the name “Snoopy” and is. five years old (line 1).
  • The bark() method is called on the my_dog object, returning “Snoopy says woof!” (line 3).
  • The get_treats() method is called on my_dog, returning “Snoopy got 3 treats” (line 5).
  • The birthday() method is called on my_dog, increasing its age by 1 (line 7).

Class Attributes

Class attributes are variables shared among all instances of a class. Unlike instance attributes, which are unique to each object, class attributes have the same value for every instance.

These attributes are useful for defining properties common to all instances of a class. They help save memory and provide a way to share data among all instances.

Class attributes are defined within the class but outside any methods. They are typically placed at the top of the class definition, immediately after the class header.

Here is the basic syntax for defining class attributes:

class ClassName:
    class_attribute = value  # Class attribute

    # Rest of the class definition
  • class_attribute = value: Defines a class attribute named class_attribute with an initial value of value.

Example:

class Dog:
    species = "Canis familiaris"  # Class attribute

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def bark(self):
        return f"{self.name} says woof!"

    def birthday(self):
        self.age += 1

    def get_treats(self, number_of_treats):
        return f"{self.name} got {number_of_treats} treats."

The example above adds the species attribute to the familiar Dog class from before (line 2). Since it specifies the name of the species, and all dogs are the same species, it’s fitting to have this attribute be a class attribute that is shared among all instances.

To access class attributes, you can use the dot notation with either the class name or an instance of the class, but it’s more common to use the class name to avoid confusion with instance attributes.

Example:

print(Dog.species)  # Accessing class attribute via class name

my_dog = Dog("Snoopy", 5)
print(my_dog.species)  # Accessing class attribute via an instance

Output:

Canis familiaris
Canis familiaris

In the example above:

  • Dog.species accesses the class attribute directly using the class name.
  • my_dog.species accesses the same class attribute using an instance of the class.

When an instance and a class have attributes of the same name, accessing the attribute from an object will return the instance attribute, if it exists; otherwise, it will return the class attribute.

Class Methods

Class methods are methods that are bound to the class rather than its instances. They can modify class state that applies across all instances of the class. Class methods have access to class attributes and other class methods, but not instance variables and methods.

These methods are marked with a @classmethod decorator to indicate that they operate on the class itself, rather than on instances of the class. You can lean more about decorators and the @ syntax in the decorators lesson, but it’s not required for the discussion here. All you need to know now, is that placing @classmethod before a method turns it into a class method.

Here is the basic syntax for defining a class method within a class:

class ClassName:
    @classmethod
    def class_method_name(cls, param1, param2, ...):
        ...  # Method body
  • @classmethod: The decorator that marks the method as a class method.
  • cls: The conventional name for the first parameter of a class method, which refers to the class itself. It’s similar to the self parameter of an instance method, but instead of holding the object, cls holds the class.

Example:

class Dog:
    species = "Canis familiaris"

    def __init__(self, name, age):
        self.name = name
        self.age = age

    @classmethod
    def change_species(cls, new_species):
        cls.species = new_species

    def bark(self):
        return f"{self.name} says woof!"

    def birthday(self):
        self.age += 1

    def get_treats(self, number_of_treats):
        return f"{self.name} got {number_of_treats} treats."

In the example above:

  • The Dog class has the class method change_species() (line 9) decorated with @classmethod.
  • Inside change_species(), cls.species = new_species changes the class attribute species to a new value specified by the caller of the method (line 10).

To call a class method, you use dot notation on the class or instance. However, It’s more common to use the class since the class method logically belongs to it.

Example:

print(Dog.species)
Dog.change_species("Snoopus fabulupus")
print(Dog.species)

Output:

Canis familiaris
Snoopus fabulupus

In the example above:

  • print(Dog.species) outputs the current value of the class attribute species (line 1).
  • Dog.change_species("Snoopus fabulupus") calls the class method change_species() and changes the species class attribute to “Snoopus fabulupus” (line 2).
  • print(Dog.species) outputs the updated value of the species class attribute (line 3).

Note that you cannot have an instance method and a class method defined with the same name in the same class. This may not be true for other programming languages, but is the case in Python. In fact, you cannot have any two methods of any type (including static methods discussed in the next section) with the same name. A method with the same name as a previous one in a class definition, simply overrides the first method and becomes the only one available.

Static Methods

Static methods are methods that belong to a class but do not have access to the class or instance attribute and methods. They are used for functions that logically belong to the class but do not need to access any class-specific data. Static methods are marked with a @staticmethod decorator.

Unlike class methods that have the cls parameter and instance methods that have the self parameter, static methods do not have any default parameter. They operate just like regular functions but are part of the class’s namespace.

A namespace is a container (such as a class) that holds a set of identifiers (names) and ensures that all names are unique within it.

Here is the basic syntax for defining a static method within a class:

class ClassName:
    @staticmethod
    def static_method_name(param1, param2, ...):
        ...  # Method body
  • @staticmethod: The decorator that marks the method as a static method.

Example:

class Dog:
    species = "Canis familiaris"

    def __init__(self, name, age):
        self.name = name
        self.age = age

    @classmethod
    def change_species(cls, new_species):
        cls.species = new_species

    def bark(self):
        return f"{self.name} says woof!"

    def birthday(self):
        self.age += 1

    def get_treats(self, number_of_treats):
        return f"{self.name} got {number_of_treats} treats."

    @staticmethod
    def is_puppy(age):
        return age < 1

In the example above:

  • The Dog class has a static method is_puppy() (line 18) decorated with @staticmethod.
  • Inside is_puppy(), it simply checks if the age provided is less than 1.

As with class methods, you can call a static method using the dot notation on the class or instance, but using the class is more common.

Example:

print(Dog.is_puppy(0.5))
print(Dog.is_puppy(2))

my_dog = Dog("Snoopy", 5)
print(my_dog.is_puppy(0.5))

Output:

True
False
True

In the example above:

  • print(Dog.is_puppy(0.5)) calls the static method is_puppy() on the Dog class and checks if 0.5 is less than 1 (line 1). The result is True.
  • print(Dog.is_puppy(2)) calls the same method and checks if 2 is less than 1 (line 2). The result is False.
  • my_dog.is_puppy(0.5) demonstrates that static methods can also be called on an instance of the class (line 5). The result is True in this case as well.

Static methods are useful for utility functions that perform a task related to the class but do not need to access or modify class attributes or instance attributes. They help keep the code organized and encapsulate functionality within the relevant class.

Special Methods

Special methods allow you to define how objects of a class behave in various situations. They enable customization of behavior for built-in Python operations such as initialization, representation, comparison, and arithmetic. Special methods are also called magic methods or dunder methods (because their names are always surrounded by double underscores).

When defined, special methods are automatically called by Python to either get information from an object or have an object do something by running its own code.

You’ve already encountered one special method in this lesson, which is __init__(). This method is called by Python when an object is created in order for the object to run its initialization code.

While there are many special methods in Python, the following subsections will discuss only two example to give you a taste of how they are used.

Special Method Example: The __str__() Method

The __str__() method defines the string representation of an object. This method is called by the built-in (i.e. always made available by Python) function str(), which converts a given object to a string. It’s also used by the print() function, which prints to the console.

Example:

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return f"I'm a Dog, my name is {self.name} and I'm {self.age} years old."

In the example above, the __str__() method (line 6) returns a string representation of the Dog object (line 7).

When a string representation of a Dog object is required, the __str__() method implemented by Dog provides it.

Example:

my_dog = Dog("Snoopy", 5)
print(my_dog)

Output:

I'm a Dog, my name is Snoopy and I'm 5 years old.

In the example above, the print() function calls __str__() of the Dog class behind the scenes (line 2) and prints what it returns to the console.

Special Method Example: The __eq__() Method

The __eq__ method defines the behavior of the equality comparison operator == for objects of the class. This method allows you to specify how objects should be compared for equality when the == operator is used.

Example:

class Dog:
    def __init__(self, name, age, owner):
        self.name = name
        self.age = age
        self.owner = owner


    def __eq__(self, other):
        return self.name == other.name and self.age == other.age

In the example above, the __eq__() method implementation (line 6) compares Dog objects based on their name and age attributes. Its only parameter aside from self, is the other object in the comparison, given as the other argument.

Although this Dog class has another attribute, owner, it is not used in the comparison. This demonstrates the flexibility of defining equality with __eq__() and that it does not necessarily mean everything is the same.

The next example demonstrates how __eq__() is used by the == operator.

Example:

dog1 = Dog("Snoopy", 5, "Bob")
dog2 = Dog("Snoopy", 5, "Alice")
dog3 = Dog("Scooby Doo", 3, "Charlie")

print(dog1 == dog2)
print(dog1 == dog3)

Output:

True
False

The example above creates three Dog objects. The first two, dog1 and dog2 (lines 1 and 2) have the same name and age, but different owners. They are therefore considered equal as defined by the __eq__() method the previous example implemented. Their equality comparison therefore returns True (line 5). The third Dog object, dog3 (line 3), however, does not have the same name and age as the first two. Consequently, its comparison with dog1 returns False (line 6).

Private Methods

Private methods are intended for internal use within a class. They are not meant to be accessed or called from outside the class.

Many programming languages prevent the usage of private methods outside the class by generating an error. However, Python does not enforce true private methods that way. Instead, it uses a naming convention to indicate that a method is intended to be private.

Naming Convention

To indicate that a method is private, its name should begin with an underscore (_). This convention signals to other programmers that the method is for internal use only. By this convention, special methods, whose name is surrounded with double underscores, are also private.

For stricter privacy in your own methods, you can use a double underscore (__) prefix. This triggers name mangling, where the interpreter changes the method name to include the class name, making it harder (though not impossible) to access from outside the class. (For now, this feature is left for you to explore further on your own.)

Using Single Underscore

The next two examples demonstrate using a single underscore to denote a private method.

Example:

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def bark(self):
        return f"{self.name} says woof!"

    def _get_dog_years(self):
        return self.age * 7

    def display_dog_years(self):
        dog_years = self._get_dog_years()
        return f"{self.name} is {dog_years} years old in dog years."

In this example:

  • The _get_dog_years() method (line 9) is intended to be private and used only within the class.
  • The display_dog_years() method (line 12) calls _get_dog_years() to get the dog’s age in dog years and returns a string.

Example:

my_dog = Dog("Snoopy", 5)
print(my_dog.display_dog_years())

Output:

Snoopy is 35 years old in dog years.

In the example above, display_dog_years() calls the _get_dog_years() method internally, demonstrating the proper use of a private method.

Inheritance

Inheritance is a feature in object-oriented programming that allows a class to inherit attributes and methods from another class. The class that is inherited from is called the parent class (also called base class or superclass), and the class that inherits from it is called the child class (also called derived class or sublcass).

Why Use Inheritance?

Inheritance promotes code reusability and helps create a hierarchical relationship between classes. For example, if you have a general class Pet that defines common attributes and methods for pets, you can create more specific classes like Dog and Cat that inherit from Pet and add their own unique attributes and methods.

Defining a Child Class

To define a child class that inherits from a parent class, use the following syntax:

class ParentClass:
    ... # Parent class code

class ChildClass(ParentClass):
    ... # Child class code

The ChildClass inherits all attributes and methods from ParentClass, which is specified in parentheses following the child class name in its definition (line 4).

The child class can also add new attributes and methods, or override ones that already exist in the parent class.

For the next example, let’s continue with our Dog class and create a parent class Pet.

Example:

class Pet:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def speak(self):
        raise NotImplementedError("Subclasses must implement this method")

In the example above:

  • The Pet class has an __init__ method that initializes the name and age attributes (lines 2-4).
  • The speak() method is defined but raises a NotImplementedError (line 7), indicating that subclasses should implement their own specific version of this method.

Now let’s create a Dog class that inherits from Pet.

Example:

class Dog(Pet):
    def __init__(self, name, age, breed):
        super().__init__(name, age)
        self.breed = breed

    def speak(self):
        return f"{self.name} says woof!"

In the example above:

  • The Dog class inherits from the Pet class (line 1).
  • The __init__() method of the Dog class calls the __init__() method of the Pet class using super(), to initialize the name and age attributes of the parent class (line 3). It then initializes the breed attribute that is added by the Dog class (line 4).
  • The speak() method (lines 6-7) overrides the method by the same name in the Pet parent class to provide a specific implementation for the Dog class.

Note: In Python, the super() function calls the method of the parent class that has the same name as the method the code is currently in. Therefore, in the previous example, within Dog‘s __init__() method, super() calls the __init__() method of the Pet class, which is the parent of Dog.

Using Inheritance

You can create instances of the Dog class and use its attributes and methods, including those inherited from the Pet class.

Example:

my_dog = Dog("Snoopy", 5, "Beagle")
print(my_dog.name)  # Inherited from Pet
print(my_dog.age)   # Inherited from Pet
print(my_dog.breed) # Defined in Dog
print(my_dog.speak()) # Overridden in Dog

Output:

Snoopy
5
Beagle
Snoopy says woof!

In the example above:

  • my_dog is an instance of the Dog class with the name “Snoopy”, age 5, and breed “Beagle” (line 1).
  • The name and age attributes are inherited from the Pet class (lines 2-3).
  • The breed attribute is defined in the Dog class (line 4).
  • The speak() method is overridden in the Dog class to provide a specific implementation for dogs (line 5).

Overriding Methods

When a child class provides a specific implementation for a method that is already defined in its parent class, it is called method overriding. This allows the child class to customize or extend the behavior of the parent class method.

In the previous example, the speak() method in the Dog class overrides the speak() method in the Pet class.

Extending the Parent Class

In addition to overriding methods, you can also add new methods and attributes to the child class that are not present in the parent class.

Let’s extend the Dog class with a new method, fetch().

Example:

class Dog(Pet):
    def __init__(self, name, age, breed):
        super().__init__(name, age)
        self.breed = breed

    def speak(self):
        return f"{self.name} says woof!"

    def fetch(self, item):
        return f"{self.name} fetched the {item}!"

Example:

my_dog = Dog("Snoopy", 5, "Beagle")
print(my_dog.fetch("ball"))

Output:

Snoopy fetched the ball!

In the example above, the fetch() method is called on the my_dog instance, demonstrating the new functionality added to the Dog class.

Multiple Inheritance

Python supports multiple inheritance, where a class can inherit from more than one parent class. This allows a child class to combine attributes and methods from multiple parent classes.

To define a child class that inherits from a parent class, use the following syntax:

class Parent1Class:
    ... # Parent 1 class code

class Parent2Class:
    ... # Parent 2 class code

class ChildClass(Parent1Class, ParentClass):
    ... # Child class code

In the multiple inheritance syntax, the parent classes are specified in a comma-separated list within the parentheses following the child class name (line 7).

Example:

class Pet:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def speak(self):
        raise NotImplementedError("Subclasses must implement this method")

class Canine:
    def __init__(self, species):
        self.species = species

    def howl(self):
        return f"{self.species} is howling!"

class Dog(Pet, Canine):
    def __init__(self, name, age, breed):
        Pet.__init__(self, name, age)
        Canine.__init__(self, "Canis familiaris")
        self.breed = breed

    def speak(self):
        return f"{self.name} says woof!"

In the example above:

  • The Dog class inherits from both the Pet and Canine classes (line 16).
  • The __init__() method of the Dog class calls the __init__() methods of both parent classes using the dot notation to initialize their attributes (lines 17-18).

Example:

my_dog = Dog("Snoopy", 5, "Beagle")
print(my_dog.name)   # Inherited from Pet
print(my_dog.age)    # Inherited from Pet
print(my_dog.species) # Inherited from Canine
print(my_dog.speak()) # Overridden in Dog
print(my_dog.howl())  # Inherited from Canine

Output:

Snoopy
5
Canis familiaris
Snoopy says woof!
Canis familiaris is howling!

In the example above:

  • my_dog is an instance of the Dog class with attributes and methods inherited from both the Pet and Canine classes (line 1).
  • The name and age attributes are inherited from the Pet class (lines 2-3).
  • The species attribute and howl method are inherited from the Canine class (lines 4 and 6).
  • The speak method is overridden in the Dog class (line 5).

Summary & Reference for Python Classes and Objects

An object is a collection of data and methods that operate on that data.


A class is a blueprint for creating objects, which are instance of a class.


To define a class, use the class keyword followed by the class name, a colon, and a code block that defines it.

class Dog:
   ...  # The code block that defines the class goes here.

To create an object (instance) of a class, use parentheses after the class name.

my_dog = Dog()

Use the __init__() method to initializes a new instance of the class and creates its instance attributes. __init__() is called automatically by Python when an object is created.

class Dog:
    def __init__(self, name):
        self.name = name

To pass values to the __init__() method, place them in the parentheses, separated by commas, when you create an object.

my_dog = Dog("Snoopy")

Instance attributes store data unique to each object. Access those using the dot notation.

print(my_dog.name)  # --> Snoopy

Instance methods are functions that are associated with an object. To define instance methods, use the def keyword. The first parameter is self and it’s used to hold the object itself.

class Dog:
    def __init__(self, name):
        self.name = name

    def bark(self):
        print(f"{self.name} says woof!")

To call an instance method on an object, use the dot notation. Don’t pass an argument for self because Python does it automatically.

my_dog.bark()  # --> Snoopy says woof!

Class attributes are shared across all instances of the class. Define those within the class outside of any method.

class Dog:
    species = "Canis familiaris"

To access class attributes use the class name or an instance.

print(Dog.species)  # --> Canis familiaris
print(my_dog.species)  # --> Canis familiaris

Class methods are methods that are bound to the class rather than its instances, and are defined with the @classmethod decorator. They receive the cls parameter, which holds the class.

class Dog:
    species = "Canis familiaris"

    @classmethod
    def common_species(cls):
        return cls.species

Static methods are methods that belong to a class but do not have access to the class or instance attribute and methods. They are defined with the @staticmethod decorator.

class Dog:
    @staticmethod
    def bark_sound():
        return "Woof!"

Static methods can be called using the class name or an instance.

print(Dog.bark_sound())  # --> Woof!
print(my_dog.bark_sound())  # --> Woof!

Special methods (a.k.a magic or methods or dunder methods) allow you to define and customize how objects of a class behave in various situations. They are always named with a double underscore (__) on each side.

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # __str__() defines the string representation of an object.
    def __str__(self):
        return f"I'm a Dog, my name is {self.name} and I'm {self.age} years old."

Private methods are methods intended for internal use within a class. They are not meant to be accessed or called from outside the class.


Unlike other programming languages, Python does not restrict access to private methods. Instead, it uses a convention of starting a method name with an underscore (_) to signal programmers that a method should be treated as private.


Inheritance allows a class to inherit attributes and methods from another class. The class that is inherited from is called the parent class (or base class), and the class that inherits from it is called the child class (or derived class).


To define a child class that inherits from a parent class, specify the name of the parent class in parentheses after the name of the child class in its definition.

class ParentClass:
    ... # Parent class code

class ChildClass(ParentClass):
    ... # Child class code

Use the super() function to call the method of the parent class that has the same name as the method the code is currently in.

class Dog(Pet):
    def __init__(self, name, age, breed):
        super().__init__(name, age)  # Calls the __init__() method of the parent class (Pet).
        self.breed = breed

    def speak(self):
        return f"{self.name} says woof!"

Python supports multiple inheritance, where a class can inherit from more than one parent class.


In the multiple inheritance syntax, the parent classes are specified in a comma-separated list within the parentheses following the child class name.

class Parent1Class:
    ... # Parent 1 class code

class Parent2Class:
    ... # Parent 2 class code

class ChildClass(Parent1Class, ParentClass):
    ... # Child class code