Сообщить об ошибке.

Объект класса и конструктор класса в Python

Содержание:


Что такое объект класса.

Объект класса в Python представляет собой ИМЯ класса, созданного (определенного/записанного) в коде. Объекты класса поддерживают два вида операций: ссылки на атрибуты и создание экземпляров.

Ссылки на атрибуты используют стандартный синтаксис, используемый для всех ссылок на атрибуты в Python: obj.name. Допустимые имена атрибутов - это все имена, которые были определены в пространстве имен класса при создании объекта класса.

Итак, если определение класса выглядит так:

class MyClass:
    """Простой пример класса"""
    i = 12345

    def f(self):
        return 'hello world'

# `MyClass` - это объект класса
>>> MyClass.i
# 12345
>>> MyClass.f
# <function MyClass.f at 0x7f3c1fd6e840>

то имя MyClass - это объект класса, а MyClass.i и MyClass.f являются действительными ссылками на атрибуты класса, возвращая целое число и функциональный объект соответственно. Атрибуты объекта класса также могут быть присвоены, так что можно изменить значение MyClass.i путем операции присвоения. __doc__ также является допустимым атрибутом, возвращающим строку документации "Простой пример класса", принадлежащую классу MyClass.

Экземпляр класса использует нотацию функций. Представьте, что объект класса - это функция без параметров, которая возвращает новый экземпляр класса. Пример ниже создает новый экземпляр класса и присваивает этот объект локальной переменной x:

x = MyClass()
>>> x.i
# 12345
>>> x.f()
# 'hello world'

Конструктор класса __init__().

Объекты классов обычно создают с экземплярами, настроенными на определенное начальное состояние. Для этого в классе Python определятся специальный метод "конструктор класса" с именем __init__(), который выполняется при создании экземпляра класса. Метод .__init__() принимает новый объект в качестве первого аргумента self.

class MyClass:
    """Простой пример класса"""
    i = 12345

    def __init__(self):
        self.data = []

    def f(self):
        return 'hello world'

Когда в классе определен метод .__init__(), то экземпляр класса автоматически вызывает .__init__() для вновь созданного экземпляра класса. Таким образом, в примере ниже, новый инициализированный экземпляр может быть получен с помощью:

>>> x = MyClass()
>>> x.data
# []

Метод __init__() может иметь аргументы для большей гибкости. В этом случае аргументы, переданные оператору создания класса, передаются в метод __init__().

Например:

>>> class Complex:
...     def __init__(self, realpart, imagpart):
...         self.r = realpart
...         self.i = imagpart
...
>>> x = Complex(3.0, -4.5)
>>> x.r, x.i
(3.0, -4.5)

Важно отметить, что без учета self, аргументы метода .__init__() - это те же самые аргументы, которые передаются при вызове конструктора класса. Таким образом, сигнатура .__init__() определяет сигнатуру конструктора класса.

Имейте в виду, что конструктор класса .__init__() не должен явно возвращать ничего отличного от None, иначе будет вызываться исключение TypeError:

>>> class Complex:
...     def __init__(self, realpart, imagpart):
...         self.r = realpart
...         self.i = imagpart
...         return realpart + imagpart

>>> x = Complex(3.0, -4.5)
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# TypeError: __init__() should return None, not 'float'

В конструкторе класса .__init__() можно выполнить любое преобразование входных аргументов, чтобы правильно инициализировать атрибуты экземпляра. Например, если пользователи будут использовать класс Rectangle, то например, можно проверить предоставленные ширину и высоту, для того, чтобы убедиться в их верном значении:

>>> class Rectangle:
...     def __init__(self, width, height):
...         if not (isinstance(width, (int, float)) and width > 0):
...             raise ValueError(f"Необходима положительная ширина, получена: {width}")
...         self.width = width
...         if not (isinstance(height, (int, float)) and height > 0):
...             raise ValueError(f"Необходима положительная высота, получена: {height}")
...         self.height = height

>>> rectangle = Rectangle(-21, 42)
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
#   File "<stdin>", line 4, in __init__
# ValueError: Необходима положительная ширина, получена: -21

Теперь предположим, что используется наследование для создания пользовательской иерархии классов и повторного использования некоторых функций в своем коде. Если подклассы предоставляют конструктор .__init__(), то он должен явно вызывать метод .__init__() базового класса с соответствующими аргументами, чтобы обеспечить правильную инициализацию экземпляров. Для этого необходимо использовать встроенную функцию super(), как в следующем примере:

>>> class Person:
...     def __init__(self, name, birth_date):
...         self.name = name
...         self.birth_date = birth_date

>>> class Employee(Person):
...     def __init__(self, name, birth_date, position):
...         super().__init__(name, birth_date)
...         self.position = position

>>> john = Employee("John Doe", "2001-02-07", "Разработчик Python")
>>> john.name
# 'John Doe'
>>> john.birth_date
# '2001-02-07'
>>> john.position
# 'Разработчик Python'