Last Updated:

Object-oriented programming in Python

Object-oriented programming in Python

Python is a new programming language. It was developed in 1991 by programmer Guido van Rossum. Unlike many other languages, in Python all entities are objects. The language also has dynamic typing and automatic memory management. The authors of the language tried to make it as convenient as possible for developers, so the syntax of the language is quite minimal. The disadvantages of Python include low speed and high memory consumption, unlike compiled languages.

Today, Python ranks 3rd in the TIOBE ranking among the most popular languages in the world. It is used in various fields: from machine learning and Big data to game and application development. Due to its simple syntax, the language is used in the process of learning programming. Python allows you to focus on learning algorithms and programming principles, rather than learning syntax.

Principles of Object-Oriented Programming

A programmer who uses Python in his projects can use different programming methodologies and paradigms. But the basis of the language is precisely the object approach. Everything is represented here as objects, even prime numbers are objects.

 

OOP principles implemented in Python:

 

  • Everything in the language is objects, even prime numbers are represented as objects. An object has a specific type, in other words, a class that implements the object's logic.
  • In addition to objects, OOP also has classes. They are denoted by the reserved word – class and are an abstract set of methods and fields.
  • Inside each object there is a layer of logic that allows you to somehow communicate with this object. This principle is called encapsulation. As part of encapsulation, the programmer can restrict access to the object's logic.
  • All objects created by the programmer must communicate with each other.
  • An object can include another object represented as a field.
  • One of the basic concepts of OOP is inheritance. It allows you to establish a parent-child relationship between objects.
  • Polymorphism, the second concept of OOP, allows the object to determine the implementation of methods.
  • Encapsulation – restricts access to data.

OOP Basics

To successfully write code in Python, you will have to understand the concept of object-oriented programming.

Classes

Classes represent an abstract structure that contains the logic for an object. Example class:

classMyClass(object):

To create a class, you specify the reserved word class, followed by a name. Parent classes can be specified in parentheses. In this example, we specify the object class as the parent. Python supports multiple inheritance.

An example of a class with inheritance:

classMyClass(parent1, parent2):

Each class has properties and methods. They are given after ":". Example of a class with properties:

classMyClass(object):
x = 5
str = ‘Hello’

An unusual feature of the language for many programmers is that the property does not need to specify a type. The language compiler will deal with the typing itself.

In addition to properties, a class can also have methods. They are set as a normal function, but with one difference. As the first parameter, the method must pass the self argument. An example of a class with a method:

classMyClass(object):
defMyMethod(self, x):

After ":" is the method code.

Objects of the class

A class object (instance) is a concrete implementation of a class. In Python, working with instances is quite simple. Example:

classSomeClass(object):
defSomeMethod(self, x):
returnx*x

To create an object of this class, you need to:

someObj = SomeClass()
and = someObj.SomeMethod(6)
print(y)

As a result of the program, the number 36 will be displayed on the screen.

To create an object, you create a variable and give it a class name. The following illustration illustrates the mechanism for accessing the members of an object when assigning a value to the variable y. Using a period, you can not only call methods and functions, but also access the fields of the object.

In Python, it is possible to create an object with some set of parameters. Example:

class Point(object):
Def __init__(self, x, y, z):
self.coord(x, y, z)
p1 = Point(13, 14, 15)

In this example, we created a Point class that will store 3 coordinates. Then, we created an instance of this p1 class. This instance represents a specific point with coordinates 13, 14, and 15. You can refer to these coordinates as follows: p1.coord

Dynamic Mutable Classes

Unlike many other languages, where a class is an immutable structure, in Python a programmer can add methods to a class while it is running. To do this, use the reserved word pass. This is how a dynamic class is created:

classSomeClass(object) :pass

Such a class is absolutely useless until we add some logic to it. An example of working with a dynamic class:

classSomeClass(object) :
pass
def method(self, x) :
return x*x
SomeClass.square = method
someObj = SomeClass
someObj.square(5)

Result of the program: 25. In this example, we supplement the class with a new method that will be accessible from any object.

Static and Class Methods

Static methods are used in many programming languages. Outwardly, they are similar to other methods, but with one difference. You can call static methods from either a class or an instance of a class. In order to create them, you need to use a special decorator: @staticmethod. Example:

classSomeClass(object) :
@staticmethod
def Hello() :
print('Hello')
SomeClass.Hello() #В result, the Hello method of the SomeClass class is called someObj
= SomeClass()
someObj.Hello() #В this line we access the Hello method, but not the class, but the instance

As a result of the program's operation, the Hello inscription will be displayed twice on the screen.

There are also class methods that run in the context of the class. To create a class method, you specify a decorator @classmethod. An important feature of such methods is the mandatory parameter-reference to the class - cls. Example:

classSomeClass(object) :
@classmethod
def Hello(cls) :
print(‘Hello, класс{}’.format(cls.__name__))
SomeClass.Hello()

As a result, "Hello, SomeClass class" will be displayed on the tap. Novice programmers do not always understand the use of class methods. The most striking example of use is the object factory.

Objects and their methods

In Python, there are quite a few special methods, for example__init__. These methods are introduced into language in order to control the life of an object.

Life of the object

The life cycle of the object will include 3 stages - creating, working and deleting them from memory. For example, it depends on the developer when the object will be initialized. To do this, you can use the __init__ method or __new__

An example of using the __init__ initializer and __new__:

classSomeClass(object):
def __new__(cls):
print(‘new’)
return super(SomeClass, cls).__new__(cls)
def __init__(self):
print(‘init’)
obj = SomeClass();

As a result of the program, new init will be displayed.

At first glance, __new__ may seem useless, but it is not. The method is useful, for example, when implementing the "Lonely" pattern. Example implementation:

classSingleton(object):
obj = None # единственный экземпляр класса
def __new__(cls, *args, **kwargs):
if cls.obj is None:
cls.obj = object.__new__(cls, *args, **kwargs)
return cls.obj
single = Singleton()
single.attr = 42
newSingle = Singleton()
newSingle.attr # 42
newSingleissingle # true

A programmer can not only create objects, but also delete them. There is a destructor method for this.
Example of deleting an object:

classSomeClass(object):
def __init__(self, name):
self.name = name
def __del__(self):
print('object {} of classSomeClass is deleted'.format(self.name))
obj = SomeClass("John");
delobj

The result of the program: the John object of the SomeClass class is deleted. However, often using destructors is not very good. Python itself identifies unnecessary objects and removes them.

Object as a function

There is a special method __call__ that allows you to call an object in the same way as a function. Example:

class Multiplier:
def __call__(self, x, y):
return x*y
multiply = Multiplier()
multiply(19, 19) # 361
multiply.__call__(19, 19) # same as in the previous line

Container simulation

Many are familiar with the len() function. It allows you to find out the length of the list from some objects, but this only works for simple types. Example:

class Collection:
def __init__(self, list):
self.list = list
collection = Collection(list)
len(collection)#в этой строке возникнет ошибка «Object of type has no len()»

This means that the Python interpreter simply doesn't understand how to count the length. To solve this problem, the method of __len__ was invented. Example without error:

class Collection:
def __init__(self, list):
self.list = list
def __len__(self):
returnlen(self.list)
collection = Collection([1, 2, 3])
len(collection) # 3

Other special methods

In addition to those described above, Python defines a large number of different methods. A developer can change the appearance of an object when printed, determine how a particular object will be converted to a string, change the way multiple instances are compared, and much more. There are a lot of these methods, they are described in detail in the language documentation.

Problem accessing attributes

An object that has multiple parent classes will also have several __getattribute__ and other special methods. How, then, does the compiler handle requests for special methods?

In order to deal with this issue, consider the obj.field request.

  1. In the beginning, the compiler calls a special method, let it be __getattribute__(field) in our case. it takes field as a parameter
  2. Then, the compiler reads all the custom attributes that are written in the __dict__.
  3. if the second step is unsuccessful the compiler tries to find the attribute in the obj.__class__.__slots__
  4. Using recursion, the attribute is searched by parent classes in the __dict__ field. If the object has more than one parent, the search for the corresponding attribute is the same as the order in which the parent classes are defined.
  5. The __getattr__ method is called if it is defined.
  6. If the previous steps did not give any result, the compiler throws an exception: AttributeError, which says that the desired attribute does not exist.

If the attribute is still found, the __get__, __set__, or __del__ method is called.

OOP Principles in Python

There are 3 main concepts for working with OOP: encapsulation, inheritance, and polymorphism. They are implemented differently in different languages.

Encapsulation

Encapsulation is a mechanism for granting access to an object's data. For example, we can declare any attribute private (that is, not available to code outside the class) using an underscore.

classSomeClass(object) :
def _Hello(sefl) : #Перед the method name is "_", that is, the
private print('Hello')
obj = SomeClass()
obj._Hello()#Метод is not actually private

In this case, we can call the Hello method on the object because it is not actually private. Access is restricted only at the level of agreement between developers. If another Python programmer sees a method whose name begins with an underscore, he will know that this method is private and should not be called.

It is possible to completely restrict access to the method. This is done with the help of two lower underscores. Example:

classSomeClass(object) :
def __Hello(sefl) :
print('Hello')
obj = SomeClass()
obj.__Hello() #method not available

The encapsulation mechanism also includes other special techniques: getters, setters, and destructors. They are needed to access the attributes of the object.

Example of working with special methods:

classSomeClass():
def __init__(self, value):
self._value = value
defgetvalue(self): # get attribute value
returnself._value
defsetvalue(self, value): # set attribute value
self._value = value
defdelvalue(self): # remove an attribute
delself._value
value = property(getvalue, setvalue, delvalue, "Property value")

This example shows three special methods: setvalue is used to assign a value to the value property, getvalue is used to read, and delvalue is a destructor that the property removes.

Inheritance

The mechanism of inheritance will allow you to establish a parent-child relationship between classes. All fields and methods of the parent class will be available in the child class.

Example of single inheritance:

class Mammal():
className = 'Mammal'
class Dog(Mammal):
species = ‘Canis lupus’
dog = Dog()
dog.className#Field className is available

In this example, the parent class is Mammal and the child class is Dog. At the same time, we can call the fields and methods of the parent class from the dog object.

In addition to single inheritance, Python supports multiple inheritance. Example:

class Horse():
isHorse = True
class Donkey():
isDonkey = True
class Mule(Horse, Donkey):
mule = Mule()
mule.isHorse #Поля доступны
mule.isDonkey

The inheritance mechanism is necessary to separate logic between classes and reuse it. For example, there is a Person class that contains an Age field and a Sleep method. There is also a Pety class in which fields from Person are available, but Pety also contains the Work method. The essence of this division lies in the fact that everyone can sleep, but not everyone can work. Peter can sleep and work.

Association

If a class has fields that are also classes, then such a mechanism will be called an association. It is divided into composition and aggregation. Often, novice programmers misuse the association, which can cause a memory leak.

Example of composition:

classSalary:
def __init__(self,pay):
self.pay = pay
defgetTotal(self):
return (self.pay*12)
class Employee:
def __init__(self,pay,bonus):
self.pay = pay
self.bonus = bonus
self.salary = Salary(self.pay)
defannualSalary(self):
return «Total: » + str(self.salary.getTotal() + self.bonus)
employee = Employee(100,10)
print( employee.annualSalary())

Example of aggregation:

class Salary(object):
def __init__(self, pay):
self.pay = pay
defgetTotal(self):
return (self.pay * 12)
class Employee(object):
def __init__(self, pay, bonus):
self.pay = pay
self.bonus = bonus
defannualSalary(self):
return «Total: » + str(self.pay.getTotal() + self.bonus)
salary = Salary(100)
employee = Employee(salary, 10)
print(employee.annualSalary())

Polymorphism

The latest concept of OOP is polymorphism. It allows the object to determine its own behavior. In Python, this is done through virtual methods. A child class can override the behavior of a method of a parent class. Such a mechanism allows you to solve the problem in different ways.

classMammal:
defmove(self):
print('Moving')
class Hare(Mammal):
def move(self):
print('Jumps')
animal = Mammal()
animal.move() # The move method of the Mammal class is called here
hare = hare()
hare.move() # And here the Hare class method is already called

As you can see from the example, the Hare child class has redefined the move method, that is, it has added its own logic to it.
To access a base class method, you can use the super reserved word.
An example with access to a base class method:

class Parent():
def __init__(self):
print(‘Parent init’)
def method1(self):
print(‘Parent method’)
class Child(Parent):
def __init__(self):
Parent.__init__(self)
def method1(self):
super(Child, self).method()
child = Child()
child.method1()

Although method1 is overridden in the child class, the base class's method will still be called.
Such methods are not the only options for implementing polymorphism. In Python, there is a so-called duck typing. It allows classes to have the same interface, but with different implementations. The interface here refers to the same name for methods and fields.

Example:

class English:
def greeting(self):
print («Hello»)
class French:
def greeting(self):
print («Bonjour»)

def intro(language):
language.greeting()
john = English()
gerard = French()
intro(john) # Hello
intro(gerard) # Bonjour

This example implements two classes, English and French. Both contain a method with the same name. Also in the program itself, the intro method is prescribed, which takes the language object as a parameter. Regardless of the type of object we passed to the intro, the greeting method will be called on the passed parameter. The main thing is that the language object contains an implementation of the method with this name.

Multiple dispatching

Thanks to virtual methods, the programmer implements single dispatching, but in Python it is possible to use multiple dispatching. This mechanism allows you to select functionality based on the number of parameters, types, and arguments.

Multiple dispatching is available in Python thanks to multi-methods. Unfortunately, the developers have not added support for multi-methods to the language, but there are a large number of libraries that allow you to use them. One of these libraries is multimethods.py.

Metaclasses

In addition to the usual classes, metaclasses are also available in Python. They differ from ordinary ones in that they use a different class as an initializer method (__init__).

Example of working with metaclasses:

classMetaClass(type):
# memory allocation for the class
def __new__(cls, name, bases, dict):
print("Creating a new class {}".format(name))
returntype.__new__(cls, name, bases, dict)
# class initialization
def __init__(cls, name, bases, dict):
print("Initializing a new class {}".format(name))
return super(MetaClass, cls).__init__(name, bases, dict)
# derivation of a class based on a metaclass
SomeClass = MetaClass("SomeClass", (), {})
# normal inheritance
classChild(SomeClass):
def __init__(self, param):
print(param)
# get an instance of the class
obj = Child("Hello")

Python is a modern interpreted language. Despite the fact that the language supports a large number of paradigms, the main concept is OOP. This paradigm includes three main concepts: encapsulation - the concealment of data, inheritance - the establishment of a parent-child connection and polymorphism - the diversity of realization.

OOP in Python has a number of features that distinguish it from other languages. For example, classes are also an object. Encapsulation is based only on agreement between developers.