Classes

Basics

While classes are interwoven into languages like Java or C, you can accomplish almost anything in Python without encountering classes.

We’ll be showing the example before explaining its contents, so don’t worry if you don’t understand what you’re looking at right away.


Anatomy of a Python Class

import math

class Pet:

    domesticated = True

    def __init__(self, name, age, gender, height, weight, is_fixed=False):
        self.name = name
        self.age = age
        self.gender = gender
        self.height = height
        self.weight = weight
        self.is_fixed = is_fixed

    def sound(self):
        return(f'Hello there, my name is {self.name}, and I\'m a pet.')

    @staticmethod
    def calculate_bmi(height, weight):
        return(weight / math.pow(height, 2) * 703)

    def is_overweight(self):
        return(self.calculate_bmi(self.height, self.weight) > 24)

    @classmethod
    def from_dict(cls, d):

        print("In from_dict, and cls is:", cls)

        is_fixed = False
        if d.get("name"):
            name = d.get("name")
        if d.get("age"):
            age = d.get("age")
        if d.get("gender"):
            gender = d.get("gender")
        if d.get("height"):
            height = d.get("height")
        if d.get("weight"):
            weight = d.get("weight")
        if d.get("is_fixed"):
            is_fixed = d.get("is_fixed")

        return cls(name, age, gender, height, weight, is_fixed)


class Dog(Pet):

    def sound(self):
        return(f'Ruff ruff!')


Instances and their Attributes

The name of a class immediately follows its declaration — this class is Pet. We would create an instance/object of Pet by doing the following:

my_pet = Pet(name="Ziva", age=.25, gender="Female", height=.5, weight=10, is_fixed=True)

The above code works thanks to the __init__ function in Pet, which allows instances of Pet to be initialized. age, name, gender, height, weight, and is_fixed are instance attributes. In this case, is_fixed is the only argument with a default value, meaning you must provide information for the other 5 in order for your code to run. We’ll explain the inner workings of __init__ shortly.

Attributes of classes can be accessed using . followed by the attribute, as in the following examples:

print(my_pet.name)
Ziva
print(my_pet.age)
0.25


Class Attributes

Python classes can have their own attributes independent of any declared objects of that class — notice how domesticated = True is on its own, outside of __init__. You can determine class attributes by calling on instances or the class itself.

print(my_pet.domesticated)  # via instance of the Pet class
print(Pet.domesticated)  # via the class itself
True
True


Class Methods

Methods are, broadly speaking, functions that are defined within a class that serve some purpose that might need repeated. All methods have the def keyword in front of them, short for define. Methods can be called in the same way as attributes. Unlike other languages, Python requires a self argument in the method declaration to ensure that an instance refers to or changes its own attributes. It is not necessary to include self anywhere except the method declaration within the class.

sound and is_overweight are some of the methods in Pet.

print(my_pet.sound())
Hello there, my name is Ziva, and I'm a pet.
print(my_pet.is_overweight())
True

Looks like our pet makes some weird sounds and needs some walks.

calculate_bmi is a static method of Pet. Static methods don’t take self or the class as arguments, nor modify the attributes of an instance or class. self is not passed as the first argument for these methods. Generally, a static method may be appropriate if the method is loosely coupled to the object.

print(Pet.calculate_bmi(50, 120))
33.744
print(my_pet.calculate_bmi(50, 120))
33.774
# this will NOT work, calculate_bmi is not passed `self`
# you will have to provide your own parameters for static methods
print(my_pet.calculate_bmi())
Error in py_call_impl(callable, dots$args, dots$keywords): TypeError: calculate_bmi() missing 2 required positional arguments: 'height' and 'weight'

Detailed traceback:
File "<string>", line 1, in <module>


from_dict is a class method of Pet. Instead of self, class methods accept cls as the first argument, and are automatically passed cls as the first argument when called. cls is simply the class, which is Pet in this case.

This method specifically takes a dictionary containing information for every instance attribute in Pet, then creates a Pet object from that.

d = {"name": "Ziva", "age": .25, "gender": "Female", "height": .5, "weight": 10, "is_fixed": True}

# Pet and cls are the same:
print(Pet)
<class '__main__.Pet'>
Pet.from_dict(d)
In from_dict, and cls is: <class '__main__.Pet'>
<__main__.Pet object at 0x7ff1a2eb3520>

Dunder methods, including __init__, all start and end with double underscores, and they generally encompass functions that are built-in to the basic object types in Python: __str__, __add__, __format__, and so on.

The idea is that you are able to flesh out your own classes by adapting base Python dunder methods for your own purposes.


Inherited Classes

At the end of our example is Dog, which is another class that contains Pet within parentheses. This makes Pet a parent class for Dog that hands down its methods and attributes.

Though Pet appears to be an argument for Dog, you cannot substitute a Pet object to initialize Dog. Child classes effectively clone their parents, overriding certain methods or attributes when necessary. In this case, sound will have different results depending on if the object is a Pet or a Dog.

my_dog = Dog("Ziva", .25, "Female", .5, 10, True)

print(my_pet.sound())
print(my_dog.sound())
Hello there, my name is Ziva, and I'm a pet.
Ruff ruff!
print(my_dog.is_overweight())
print(my_dog.domesticated)
True
True


Resources

A great introduction to classes in Python.

A good resource for the basics.

A nice article explaining classes and object oriented programming.

A great explanation of the differences between the types of methods.