What is the __init__ method in Python?
The __init__ method is known as the constructor in a class, it is called every time a new instance is created. Within the constructor we can pass arguments that every instance of the given class will have once instantiated. However, it is not necessary to pass any arguments to this function. See the example class below:
class Example:
def __init__(self):
print('Nothing necessary here except self')
ex = Example()
The variables that are created within this constructor are known as instance attributes. Take the following Person class, which takes a person's first and last name along with their age.
class Person:
def __init__(self, first_name, last_name, age):
print('calling the __init__ method')
self.first_name = first_name
self.last_name = last_name
self.age = age
The print statement has been added to show when the __init__ is called, notice that when you run the block of code above, the __init__ method hasn't been called yet. Let's create an instance of the Person class:
p1 = Person('Harry', 'Potter', 14)
When the line of code above is executed the __init__ is called since we are creating a new instance. The variables we pass in as arguments have been set. You may have noticed an error when using third party packages that looks something like the following:
TypeError: __init__() missing 1 required positional argument:
This means that an argument that the constructor was expecting hasn't been supplied when creating an instance of the class. You can replicate this error by create an instance of the Person class without supplying one of the variables.
It is now possible to access the variables using the following syntax:
p1.age #returns 14
p1.first_name # returns Harry
p1.last_name# returns Potter
The variables above are known as instance attributes.
Public and Private Variables
It isn't possible to make a variable truly private in Python as it is in some other languages. With that being said, sometimes you will see a variable in the constructor that has an underscore or double underscore prefixed to the name. Making the Person class' instance variables semi-private:
class Person:
def __init__(self, first_name, last_name, age):
print('calling the __init__ method')
self._first_name = first_name
self.__last_name = last_name
self._age = age
If we try to call the age attribute the same way it will raise an attribute error:
p1 = Person('Harry', 'Potter', 14)
p1.age
AttributeError: 'Person' object has no attribute 'age'
The reason that these variables aren't truly private is that we can still access them by typing the _ or __ before the variables name as shown below:
p1._age # returns 14
Class Attributes
Class attributes are attributes which are created when the class itself is created. These attributes don't require an object to be instantiated before they are created. With this being said, we can surmise that these variables should be something that all objects will have in common. In the case of the Person class we have been working with, the species of all people will be human, so we can set it as a class variable.
class Person:
print('setting class attributes')
species = 'Human'
def __init__(self, first_name, last_name, age):
print('calling the __init__ method')
self.first_name = first_name
self.last_name = last_name
self.age = age
Notice that as soon as the class above is created the species attribute, in this case a class variable is set straightaway.
The difference between class attributes and instance attributes is that we don't need to create an instance to call the class variables, see an example below:
Person.species # returns 'Human'
If we tried to use the class to call the name or age variables we would get an AttributeError:
Person.age
#returns
#AttributeError: type object 'Person' has no attribute 'age'
The class attributes can also be accessed through instances
p1 = Person('Harry', 'Potter', 14)
p1.species
#returns 'Human'
This is because the species has been included in the p1 object. We can check this by looking at the methods and attributes associated with the object.
dir(p1)
['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'age',
'first_name',
'last_name',
'species']
We can also use the __dict__ method above on the instance, which will return the instance dictionary, which is a dictionary containing all instance variables. Notice that the species variable hasn't been included since it is not a class variable
p1.__dict__
Out:
{'first_name': 'Harry', 'last_name': 'Potter', 'age': 14}
If we want to see all the class attributes we can one of two following methods:
# calling from an instance
p1.__class__.__dict__
# calling from class
Person.__dict__
Both of which will return the following output:
mappingproxy({'__module__': '__main__',
'species': 'Human',
'__init__': <function __main__.Person.__init__(self, first_name, last_name, age)>,
'__dict__': <attribute '__dict__' of 'Person' objects>,
'__weakref__': <attribute '__weakref__' of 'Person' objects>,
'__doc__': None})
Summary
- The __init__ method is known as the constructor and we can set instance attributes using it.
- We don't need an instance to access class attributes.
- Python doesn't have private variables, but the convention is to use an _ before the variable name to indicate it is private.