A class is defined with the keyword
class. The syntax is as follows:
class NameOfClass: # class defition goes here
Classes are usually named in camel case. This means that all the words in the class name are written together, without spaces, and each word is capitalised. The following class names follow this convention:
A single class definition should represent a single whole, the contents of which should be atomically linked together in some way. In more complicated programs classes can contain members of other classes. For example, the class
Course could contain objects of class
Lets have a look at a skeleton of a class definition. The functionalities are still missing at this point.
class BankAccount: pass
The above piece of code tells Python that here we are defining a class named
BankAccount. The class does not contain any functionality yet, but we can still create an object based on the class.
Lets have a look at a program where two variables are added to a
owner. Any variables attached to an object are called its attributes, or more specifically, data attributes, or sometimes instance variables.
The attributes attached to an object can be accessed through the object:
class BankAccount: pass peters_account = BankAccount() peters_account.owner = "Peter Python" peters_account.balance = 5.0 print(peters_account.owner) print(peters_account.balance)
Peter Python 5.0
The data attributes are available only through the object they are attached to. Each
BankAccount object created based on the
BankAccount class has its own values attached to the data attributes. Those values can be accessed by referring to the object in question:
account = BankAccount() account.balance = 155.50 print(account.balance) # This refers to the data attribute balance attached to the account print(balance) # THIS CAUSES AN ERROR, as there is no such independent variable available, and the object reference is missing
Adding a constructor
In the above example we saw that a new instance of a class can be created by calling the constructor method of the class like so:
NameOfClass(). Above we then attached data attributes to the object separately, but it is often more convenient to pass these initial values of attributes directly as the object is created. In the above example we first had a
BankAccount object without these attributes, and the attributes only existed after they were explicitly declared.
Declaring attributes outside the constructor results in a situation where different instances of the same class can have different attributes. The following code produces an error because we now have another
paulas_account, which does not contain the same attributes:
class BankAccount: pass peters_account = BankAccount() peters_account.owner = "Peter" peters_account.balance = 1400 paulas_account = BankAccount() paulas_account.owner = "Paula" print(peters_account.balance) print(paulas_account.balance) # THIS CAUSES AN ERROR
So, instead of declaring attributes after each instance of the class is created, it is usually a better idea to initialize the values of the attributes as the class constructor is called. As the
BankAccount class definition is currently just a skeleton, the constructor method is implicitly assumed by the Python interpreter, but it is possible to define your own constructor methods, and that is exactly what we will do now.
A constructor method is a method declaration with the special name
__init__, usually included at the very beginning of a class definition.
Lets have a look at a
BankAccount class with a constructor method added in:
class BankAccount: # The constructor def __init__(self, balance: float, owner: str): self.balance = balance self.owner = owner
The name of the constructor method is always
__init__. Notice the two undescores on both sides of the word
The first parameter in a constructor definition is always named
self. This refers to the object itself, and is necessary for declaring any attributes attached to the object. The assignment
self.balance = balance
assigns the balance received as an argument to the balance attribute of the object. It is a common convention to use the same variable names for the parameters and the data attributes defined in a constructor, but the variable names
balance above refer to two different variables:
self.balanceis an attribute of the object. Each
BankAccountobject has its own balance.
balanceis a parameter in the constructor method
__init__. Its value is set to the value passed as an argument to the method as the constructor is called (that is, when a new insctance of the class is created).
Now that we have defined the parameters of the constructor method, we can pass the desired initial values of the data attributes as arguments as a new object is created:
class BankAccount: # The constructor def __init__(self, balance: float, owner: str): self.balance = balance self.owner = owner # As the method is called, no argument should be given for the self parameter # Python assigns the value for self automatically peters_account = BankAccount(100, "Peter Python") paulas_account = BankAccount(20000, "Paula Pythons") print(peters_account.balance) print(paulas_account.balance)
It is now much easier to work with the
BankAccount objects, as the values can be passed at object creation, and the resulting two separate instances can be handled more predictably and uniformly. Declaring data attributes in the constructor also ensures the attributes are actually declared, and the desired initial values are always given by the programmer using the class.
It is still possible to change the initial values of the data attributes later in the program:
class BankAccount: # The constructor def __init__(self, balance: float, owner: str): self.balance = balance self.owner = owner peters_account = BankAccount(100, "Peter Python") print(peters_account.balance) # Change the balance to 1500 peters_account.balance = 1500 print(peters_account.balance) # Add 2000 to the balance peters_account.balance += 2000 print(peters_account.balance)
100 1500 3500
Let's have a look at another example of classes and objects. We'll write a class which models a single draw of lottery numbers:
from datetime import date class LotteryDraw: def __init__(self, round_week: int, round_date: date, numbers: list): self.round_week = round_week self.round_date = round_date self.numbers = numbers # Create a new LotteryDraw object round1 = LotteryDraw(1, date(2021, 1, 2), [1,4,8,12,13,14,33]) # Tulostetaan tiedot print(round1.round_week) print(round1.round_date) for number in round1.numbers: print(number)
1 2021-01-02 1 4 8 12 13 14 33
As you can see above, the attributes can be of any type. Here, each LotteryDraw object has attributes of type
Using objecs formed from your own classes
Objects formed from your own class definitions are no different from any other Python objects. They can be passed as arguments and return values just like any other object. We could, for example, write some helper functions for working with bank accounts:
# this function creates a new bank account object and returns it def open_account(name: str): new_account = BankAccount(0, name) return new_account # this function adds the amount passed as an argument to the balance of the bank account also passed as an argument def deposit_money_on_account(account: BankAccount, amount: int): account.balance += amount peters_account = open_account("Peter Python") print(peters_account.balance) deposit_money_on_account(peters_account, 500) print(peters_account.balance)
You can check your current points from the blue blob in the bottom-right corner of the page.