Python

Python Class Example

In this tutorial, we’ll talk about classes. By using Python 3.4, we will talk about what classes are, how do we use them and what can we do with them.

Python is an object-oriented programming language, and as such it has a class mechanism. Classes are extensible templates for creating objects, providing initial values for state (variables) and implementation of behavior (functions and methods).
 
 
 
 


 
Compared with other languages, Python’s class mechanism adds classes with a minimum of new syntax. See:

class ClassName:
    body

The only new thing we need to learn to create a class as simple as this one is the keyword class, pretty simple. To get an instance of this class, we don’t need to add a new keyword to our dictionary, we just do my_var = ClassName(), and now my_var is an instance of ClassName. Still simple.

Let’s talk about the body of a class. When we define a class, its body is executed, in other words if we write a script that contains only the next piece of code:

simple.py

class MyBodyWillBeExecuted:
    print("MyBodyWillBeExecuted is being defined")

By running it, like python3 simple.py, the output will say:

$ python3 simple.py
MyBodyWillBeExecuted is being defined

And that is actually how class variables are defined, but before we see an example, let’s see a definition. Class variables are variables that are available at class level, all instances of a class will be able to access these variables. If you come from Java, you can think of them as static variables. Now, let’s see:

simple.py

class MyBodyWillBeExecuted:
    class_variable = "a value"

Now MyBodyWillBeExecuted has a class variable, every instance of MyBodyWillBeExecuted will be able to read class_variable. Now, as in every other OOP language that I know of, classes in Python have constructors.

A constructor in Python is a function that is executed when an instance of its class is created, and is always called __init__, let’s see:

simple.py

class MyBodyWillBeExecuted:
    print("this print is executed when the class is defined")
    class_variable = "a value"

    def __init__(self):
        print("this print is executed when an instance of this class is created")

a = MyBodyWillBeExecuted()
b = MyBodyWillBeExecuted()

That __init__(self) is the constructor of our class. This function receives a parameter, self, we’ll talk about this later. Now, let’s see the output of this script:

$ python3 simple.py
this print is executed when the class is defined
this print is executed when an instance of this class is created
this print is executed when an instance of this class is created

As you see, the “this print is executed when the class is defined” message is printed once when we defined the class, and the “this print is executed when an instance of this class is created” message is printed twice, once for every instance of MyBodyWillBeExecuted we create.

Let’s talk about that self argument. In Python, there are no shorthands for referencing the object’s members from its methods/functions, the method/function is defined with an explicit first argument representing the object, which is provided implicitly by the call. This is not specific to the __init__ method, but for all instance methods. This might seem a little weird, but you’ll get the hang of it by practice.

Now that we’ve cleared out that self argument, we can talk about instance variables. To create a variable in an instance of a class, you need to assign its value to a property of self. Let’s see:

simple.py

class MyBodyWillBeExecuted:
    class_variable = "a value"

    def __init__(self, n):
        self.n = n

a = MyBodyWillBeExecuted(1)
b = MyBodyWillBeExecuted(2)

print(a.n)
print(b.n)

The new script changed the constructor signature, as it now receives an argument n and assigns it to self.n. That self.n will be available in every instance of that class, as we see in the print statements that are printing a.n and b.n. The output below.

$ python3 simple.py
1
2

Now, let’s access that variable from another method, called add_one:

simple.py

class MyBodyWillBeExecuted:
    class_variable = "a value"

    def __init__(self, n):
        self.n = n

    def add_one(self):
        self.n += 1
        return self.n

a = MyBodyWillBeExecuted(1)
b = MyBodyWillBeExecuted(2)

while a.n < 10:
    a.add_one()

print(a.n)
print(b.n)

From another function now, we are accessing the instance variable n, by doing self.n. Let’s see the output.

$ python3 simple.py
10
2

One thing that’s worth noticing here, is that we have made all variables and functions public until now… well, that’s actually not optional, Python doesn’t provide a way of making things private. Instead, there is a convention, every name prefixed with an underscore should be treated as a non-public part of the class.

Let’s see an example of this:

simple.py

class MyBodyWillBeExecuted:
    class_variable = "a value"

    def __init__(self, n):
        self.n = n

    def _add(self, n):
        self.n += n

    def add_one(self):
        self._add(1)
        return self.n

a = MyBodyWillBeExecuted(1)
b = MyBodyWillBeExecuted(2)

while a.n < 10:
    a.add_one()

b._add(1)

print(a.n)
print(b.n)

We defined a new method called _add that receives a number as argument and adds it to self.n. This method will be available outside of the instance, as you can see when we do b._add(1), but you shouldn’t call this method from outside, it’s a convention that almost every Python programmer knows and follows.

Also, Python provides a mechanism to hide things to prevent them from being accessed from outside a class, but let’s make it clear, it is not making anything private. If you name a member of a class with two underscores as prefix, Python will internally change its name to include the class name, this means that if you have an attribute called __private_member in a class called AClass, Python will internally rename this attribute _AClass__private_member and refactor every internal calls, but not external ones. Of course, you can still access this member by doing instance._AClass__private_member, so it is not actually private. I don’t really do this often, but maybe I should, I just really don’t see the point.

Let’s do some recap. We’ve talked about classes syntax, constructors, class variables and instance variables and instance methods/functions, let’s talk about destructors.

As you might know, Python deletes unreferenced objects automatically to free memory space (garbage collection). This process runs during program execution and it’s triggered when an object’s reference count reaches zero. An object’s reference count increases when it is assigned a new name or placed in a container (list, tuple, or dictionary). The object’s reference count decreases when it’s deleted with del, its reference is reassigned, or its reference goes out of scope. When an object’s reference count reaches zero, Python collects it automatically.

In most cases (integers, strings, list, dictionaries, etc.), you won’t notice when an object is deleted, but a class can implement the __del__ method, which will be invoked right before the instance is destroyed. This method was meant to be used to release non memory resources an instance may use (files, sockets, etc.). Let’s see:

simple.py

class MyBodyWillBeExecuted:
    class_variable = "a value"

    def __init__(self, n):
        self.n = n

    def _add(self, n):
        self.n += n

    def add_one(self):
        self._add(1)
        return self.n

    def __del__(self):
        print("I'm being destroyed... Goodbye world!")

a = MyBodyWillBeExecuted(1)
b = MyBodyWillBeExecuted(2)

while a.n < 10:
    a.add_one()

b._add(1)

print(a.n)

a = None

print(b.n)

Every instance of this class will print “I’m being destroyed… Goodbye world!” when Python is about to destroy it and reclaim the memory consumed by it. So, let’s predict what is going to happen:

  • The class MyBodyWillBeExecuted is defined and two instances of it are created (a and b).
  • The method a.add_one() is called until a.n is 10 and b._add(1) is executed.
  • The value of a.n is printed, and then None is assigned to a. Now the instance of MyBodyWillBeExecuted that was referenced by a is an orphan, no one is pointing to it, so it will be destroyed.
  • The value of b.n is printed.
  • The program, about to be terminated, will destroy the remaining instance of MyBodyWillBeExecuted.

Let’s see the output to check this program behaves as expected:

$ python3 simple.py
10
I'm being destroyed... Goodbye world!
3
I'm being destroyed... Goodbye world!

Now, let’s see a complete example of what we’ve seen here. We’ll write a class User, which will contain a name and a password, we’ll see what to do with it later.

basic.py

...
class User:

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

It’s a simple class, with two instance variables, nothing too hard. Now, passwords are supposed to be stored hashed, so, let’s do that. Let’s write a class called Hasher, that will hold an instance variable salt and a function hash.

basic.py

...
import hashlib, binascii
...
class Hasher:

    def __init__(self, salt):
        self._salt = salt

    def hash(self, value):
        bin_hash = hashlib.pbkdf2_hmac('sha512', value.encode('utf-8'), self._salt, 100000)
        return binascii.hexlify(bin_hash).decode('utf-8')
...

The constructor receives a variable called salt and stores it in an instance variable. The function hash receives a value, uses pbkdf2 from hashlib (a module provided by Python) to hash it, and then the module binascii to translate the returned binary into a hex string. The resulting string is returned.

Now we need a place to store this users, let’s call it UserStorage. It will store users in a dictionary, indexed by the user’s name, to guarantee that it will be unique.

basic.py

...
class UserStorage:

    def __init__(self):
        self._store = {}

    def store(self, user):
        self._store[user.name] = user

    def find(self, user_name):
        return self._store.get(user_name)
...

It isn’t doing anything strange, there is a constructor that initializes an instance variable _store as an empty dictionary, then there are two functions, one to store an user and another one to retrieve a user by its name. Let’s glue it all together in a GUI (Graphic User Interface).

We’ll write a class GUI that will print and prompt stuff from the console.

basic.py

...
import getpass
...
class GUI:

    def __init__(self, storage, hasher):
        self._storage = storage
        self._hasher = hasher

    def _create_user(self):
        user_name = input("user name: ")
        password = self._hasher.hash(getpass.getpass(prompt="password: "))
        user = User(user_name, password)
        self._storage.store(user)

    def _check_login(self):
        user_name = input("user name: ")
        password = self._hasher.hash(getpass.getpass(prompt="password: "))
        user = self._storage.find(user_name)
        if user is None or user.password != password:
            print("login failed")
        else:
            print("login successful")

    def main_menu(self):
        print("1. Create User\n2. Check Login\n0. Exit")
        option = int(input("what do you want to do? "))
        if option == 1:
            self._create_user()
        elif option == 2:
            self._check_login()
        if option != 0:
            self.main_menu()
...

This class has a constructor that receives a Hasher and a UserStorage, and holds them in instance variables. There is one “public” function called main_menu that will print the options and take action according user’s input, it’s a recursive function that will keep calling itself until the user inputs 0.

If the user inputs 1, main_menu will call self._create_user, a “private” method that will ask the user for the user name and password, hash the password, create the User and store it.

If the user inputs 2, main_menu will call self._check_login, another “private” method that will ask the user for credentials and check them with the UserStorage.

To ask for passwords, this class uses a module called getpass provided by Python, which is a tool to ask for passwords from the terminal, passwords written in the terminal when prompted with getpass will not be shown. Let’s see how the whole code looks like.

import hashlib, binascii, getpass


class Hasher:

    def __init__(self, salt):
        self._salt = salt

    def hash(self, value):
        bin_hash = hashlib.pbkdf2_hmac('sha512', value.encode('utf-8'), self._salt, 100000)
        return binascii.hexlify(bin_hash).decode('utf-8')


class User:

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


class UserStorage:

    def __init__(self):
        self._store = {}

    def store(self, user):
        self._store[user.name] = user

    def find(self, user_name):
        return self._store.get(user_name)


class GUI:

    def __init__(self, storage, hasher):
        self._storage = storage
        self._hasher = hasher

    def _create_user(self):
        user_name = input("user name: ")
        password = self._hasher.hash(getpass.getpass(prompt="password: "))
        user = User(user_name, password)
        self._storage.store(user)

    def _check_login(self):
        user_name = input("user name: ")
        password = self._hasher.hash(getpass.getpass(prompt="password: "))
        user = self._storage.find(user_name)
        if user is None or user.password != password:
            print("login failed")
        else:
            print("login successful")

    def main_menu(self):
        print("1. Create User\n2. Check Login\n0. Exit")
        option = int(input("what do you want to do? "))
        if option == 1:
            self._create_user()
        elif option == 2:
            self._check_login()
        if option != 0:
            self.main_menu()

if __name__ == '__main__':
    user_storage = UserStorage()
    hasher = Hasher(b"some_salt")
    GUI(user_storage, hasher).main_menu()

The main code initialized both the UserStorage and the Hasher and then creates the GUI and starts the main_menu. Let’s see the output.

$ python3 basic.py
1. Create User
2. Check Login
0. Exit
what do you want to do? 1
user name: svinci
password:
1. Create User
2. Check Login
0. Exit
what do you want to do? 2
user name: svinci
password:
login successful
1. Create User
2. Check Login
0. Exit
what do you want to do? 2
user name: svinci
password:
login failed
1. Create User
2. Check Login
0. Exit
what do you want to do? 2
user name: asd
password:
login failed
1. Create User
2. Check Login
0. Exit
what do you want to do? 0

This input shows how to create a user, and then tests a successful login, a login with the wrong password and another one with the wrong user name. Let’s jump to the next subject now, which is inheritance.

In Python, as in most OOP languages, you can write classes that inherit from other classes. If you have a class Car and another class Bus, you’ll find that they have a lot in common, and that’s because they are both vehicles, so it would be a good idea to make them inherit from a class Vehicle. Let’s see it in practice.

We’ll write a class Vehicle that holds the information every vehicle has, no matter if it’s a bus, a car or a motorcycle. Then, this class will define the behavior every vehicle has: accelerator, breaks, honk and speedometer.

inheritance.py

class Vehicle:

    def __init__(self, wheels, max_speed, acceleration, breaks_acceleration, brand, model):
        self.wheels = wheels
        self.max_speed = max_speed
        self.acceleration = acceleration
        self.breaks_acceleration = breaks_acceleration
        self.current_speed = 0
        self.brand = brand
        self.model = model

    def accelerator(self, target_speed):
        while self.current_speed < target_speed and self.current_speed < self.max_speed:
            self.current_speed += self.acceleration
        self.speedometer()

    def breaks(self, target_speed=0):
        while self.current_speed > 0 and self.current_speed > target_speed:
            self.current_speed -= self.breaks_acceleration
        self.speedometer()

    def speedometer(self):
        print("{}, {}: {} kmph".format(self.brand, self.model, self.current_speed))

    def honk(self):
        print("HONK!")
...

It’s pretty basic, but it will do the job. As you see, this class is written as any other class we’ve wrote so far, not additional syntax is necessary to be able to inherit from a class. As I’m from Argentina, the speed will be measured in kilometers per hour (kmph). Now, let’s see, having this class, how we can write Car and Bus.

inheritance.py

...
class Car(Vehicle):
    def __init__(self):
        Vehicle.__init__(self, 4, 130, 10, 30, "Ford", "Focus")


class Bus(Vehicle):
    def __init__(self):
        Vehicle.__init__(self, 4, 80, 5, 50, "Mercedes", "Don't Know any model of bus")
...

Those are some really tiny classes there, but they are powerful enough for us, as they hold all the variables and behavior as a Vehicle.

As you might see, to inherit from a class we write its name between parentheses between the name of our class and the colon. Then, in the constructor, we need to call our parent class’ constructor giving self as first argument. Let’s see it all together now.

inheritance.py

class Vehicle:

    def __init__(self, wheels, max_speed, acceleration, breaks_acceleration, brand, model):
        self.wheels = wheels
        self.max_speed = max_speed
        self.acceleration = acceleration
        self.breaks_acceleration = breaks_acceleration
        self.current_speed = 0
        self.brand = brand
        self.model = model

    def accelerator(self, target_speed):
        while self.current_speed < target_speed and self.current_speed < self.max_speed:
            self.current_speed += self.acceleration
        self.speedometer()

    def breaks(self, target_speed=0):
        while self.current_speed > 0 and self.current_speed > target_speed:
            self.current_speed -= self.breaks_acceleration
        self.speedometer()

    def speedometer(self):
        print("{}, {}: {} kmph".format(self.brand, self.model, self.current_speed))

    def honk(self):
        print("HONK!")


class Car(Vehicle):

    def __init__(self):
        Vehicle.__init__(self, 4, 130, 10, 30, "Ford", "Focus")


class Bus(Vehicle):
    def __init__(self):
        Vehicle.__init__(self, 4, 80, 5, 50, "Mercedes", "Don't Know any model of bus")

if __name__ == '__main__':
    bus = Bus()
    car = Car()
    car.accelerator(40)
    bus.accelerator(40)
    car.breaks(20)
    bus.honk()
    car.accelerator(40)

In the main code, we instantiate a Bus and a Car, the car then goes at 40 kmph, the bus follows along, the car breaks, the bus honks so the car accelerates again. Let’s see the output.

$ python3 inheritance.py
Ford, Focus: 40 kmph
Mercedes, Don't Know any model of bus: 40 kmph
Ford, Focus: 10 kmph
HONK!
Ford, Focus: 40 kmph

Great. We’ve got these vehicles working, but wait, what if our bus have some super futuristic breaks that actually have no acceleration, but changes the speed to 0 instantly? Everyone on the bus would probably die, but still, let’s do it. Let’s override the breaks.

You can always override your parent class methods. One reason for overriding parent’s methods is because you may want special or different functionality in your subclass. In Python no keywords or special syntax is needed, we just re define the method we want to override, see.

inheritance.py

...
class Bus(Vehicle):
    def __init__(self):
        Vehicle.__init__(self, 4, 80, 5, 50, "Mercedes", "Don't Know any model of bus")

    def breaks(self, target_speed=0):
        print("SUPER AWESOME BREAKS!")
        self.current_speed = target_speed
        self.speedometer()
...

This bus will break a lot faster than any other vehicle. Let’s use these new breaks by changing our main code.

inheritance.py

...
if __name__ == '__main__':
    bus = Bus()
    car = Car()
    car.accelerator(40)
    bus.accelerator(40)
    car.breaks(20)
    bus.honk()
    car.accelerator(40)
    bus.breaks(0)
...

The new output can be found below.

$ python3 inheritance.py
Ford, Focus: 40 kmph
Mercedes, Don't Know any model of bus: 40 kmph
Ford, Focus: 10 kmph
HONK!
Ford, Focus: 40 kmph
SUPER AWESOME BREAKS!
Mercedes, Don't Know any model of bus: 0 kmph

The behavior remains the same for all methods, except for the breaks of the bus. Now, our car’s accelerator is broken, it adds 10 kmph to the target speed when it’s accelerated. Let’s write that.

inheritance.py

...
class Car(Vehicle):
    def __init__(self):
        Vehicle.__init__(self, 4, 130, 10, 30, "Ford", "Focus")

    def accelerator(self, target_speed=0):
        super().accelerator(target_speed + 10)
...

To call our parent class’ method accelerator within our child class, we use the built-in function super(). Let’s see the behavior of this.

$ python3 inheritance.py
Ford, Focus: 50 kmph
Mercedes, Don't Know any model of bus: 40 kmph
Ford, Focus: 20 kmph
HONK!
Ford, Focus: 50 kmph
SUPER AWESOME BREAKS!
Mercedes, Don't Know any model of bus: 0 kmph

Now our car is 50 kmph instead of 40 kmph, just as we… wanted? Well, it is what it is, we need to take it to the mechanic.

3. Download the Code Project

This was an example on Classes in Python.

Download
You can download the full source code of this example here: python-classes

Sebastian Vinci

Sebastian is a full stack programmer, who has strong experience in Java and Scala enterprise web applications. He is currently studying Computers Science in UBA (University of Buenos Aires) and working a full time job at a .com company as a Semi-Senior developer, involving architectural design, implementation and monitoring. He also worked in automating processes (such as data base backups, building, deploying and monitoring applications).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button