Если вы давно изучаете программирование на Python, то встречали слово «self». Оно встречается всякий раз, когда вы работаете над объектно-ориентированным программированием на Python. Вы видели его в определениях методов и инициализации переменных. Но прежде, чем говорить о том, что означает и как использовать параметр self в Python, давайте разберемся, что такое переменные класса и экземпляра и что такое методы класса и экземпляра.
Переменные класса в Python
Переменные класса — это тип переменных, совместно используемых всеми экземплярами этого класса. Если вы обращаетесь к переменным класса из экземпляров, то значение будет таким же. Переменные класса определяются сразу после определения класса и вне каких-либо методов или функций.
|
1 2 3 4 |
class MyClass: var_1 = "COVID_19" var_2 = 100 var_3 = False |
В этом коде var_1, var_2 и var_3 являются переменными класса.
Переменные экземпляра в Python
Переменные экземпляра — это переменные, в которых все экземпляры хранятся сами по себе (т. е. конкретный объект владеет своими переменными экземпляра). Таким образом, значения переменных отличаются от экземпляра к экземпляру.
|
1 2 3 4 5 6 7 8 9 10 11 12 |
class MyClass: var_1 = "COVID_19" var_2 = 100 var_3 = False def __init__(self, param1, param2): self.instance_var1 = param1 # instance_var1 is a instance variable self.instance_var2 = param2 # instance_var2 is a instance variable |
В этом коде переменными экземпляра являются instace_var1 и instance_var2.
В отличие от переменных класса, переменные экземпляра должны быть определены внутри методов.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class MyClass: var_1 = "COVID_19" var_2 = 100 var_3 = False def __init__(self, param1, param2, param3): self.instance_var1 = param1 # instance_var1 is a instance variable self.instance_var2 = param2 # instance_var2 is a instance variable self.instance_var3 = param3 # instance_var3 is a instance variable obj1 = MyClass('Disease', 150, True) print('The class variable is: ', obj1.var_1) print('The instance variable is: ', obj1.instance_var1) obj2 = MyClass('Rasparatory', 200, False) print('The class variable is: ', obj2.var_1) print('The instance variable is: ', obj2.instance_var2) |
В этом примере мы определили три переменные класса и три переменные экземпляра.
Затем мы создали два экземпляра с именами obj1 и obj2. Теперь, если вы обращаетесь к переменным класса с помощью obj1 и obj2, значения будут одинаковыми. Итак, в нашем примере у нас есть доступ к var_1 и var_2 с разными экземплярами, и значения такие же, как вы видите в выводе.
Но когда мы обращаемся к переменным экземпляра с разными экземплярами, значения различны. Это связано с тем, что мы предоставляем значения через метод __init__(), и поэтому значения различаются от экземпляра к экземпляру.
Методы класса в Python
Методы класса полезны для установки или получения сведений(статуса) о классе. Метод класса имеет определенный параметр, который следует поместить в качестве первого параметра. Это параметр cls, представляющий класс.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class MyClass: var_1 = "COVID_19" var_2 = 100 var_3 = False @classmethod def class_method(cls): print("the class method was called") return 1 print(MyClass.class_method()) print(MyClass.var_1) print(MyClass.var_2) print(MyClass.var_3) |
В этом примере мы определили метод класса с помощью декоратора @classmethod. Метод класса здесь class_method, который принимает cls в качестве параметра. Это то же самое, что и self при создании метода экземпляра.
Итак, когда мы определяем метод класса, мы передаем cls в качестве первого параметра, а когда мы определяем метод экземпляра, мы передаем self в качестве первого параметра.
Затем мы получили доступ к методу класса, используя только класс, а не объект. Мы также получили доступ к переменным класса, используя class.
Итак, даже не создавая экземпляр объекта, мы можем получить доступ к методам класса следующим образом:
|
1 |
MyClass.class_method() |
Методы экземпляра
Мы видели переменные экземпляра, переменные класса и методы класса. Теперь мы увидим методы экземпляра.
При определении метода экземпляра первым параметром метода всегда должно быть self. Тем не менее, вы можете назвать это как угодно и не обязательно self, но то, что представляет этот аргумент, всегда будет одним и тем же. И это лучшая идея для того, чтобы придерживаться self, поскольку это глобальная конвенция.
|
1 2 3 4 5 6 7 8 |
class MyClass: def add(self, a, b): return a + b obj = MyClass() print(obj.add(11, 21)) |
В этом примере мы определили метод экземпляра с именем add(), который принимает два параметра. Затем мы создали экземпляр объекта и вызвали метод add().
Итак, как вы можете заметить из приведенного выше кода, при определении метода экземпляра первым параметром является self; когда мы вызываем этот метод, мы ничего не передаем для self в качестве аргументов.
Что такое self в Python?
Ключевое слово self представляет экземпляр(объект) данного класса. Например, в приведенном выше случае объект obj имеет собственные атрибуты a и b. Если бы не было собственного аргумента, то один и тот же класс не мог бы содержать информацию для обоих этих объектов.
Однако, поскольку класс является планом, self позволяет получить доступ к атрибутам и методам каждого объекта в Python. Это позволяет каждому объекту иметь свои атрибуты и методы. Даже задолго до создания этих объектов мы ссылаемся на объекты как на себя при определении класса.
Явное определение self
Даже когда мы знаем, как использовать self, это может показаться странным, особенно программистам из других языков программирования. Self передается в качестве аргумента явно каждый раз, когда мы определяем метод экземпляра.
|
1 2 3 4 5 6 7 8 9 10 11 12 |
class MyClass: def __init__(self, a, b): self.a = a self.b = b def multi(self): return self.a * self.b obj = MyClass(3, 7) print('The multiplication of a and b is: ', obj.multi()) |
Если вы запустите приведенный выше код, вы получите следующий вывод:
|
1 |
The multiplication of a and b is: 21 |
В приведенном выше примере __init__() определяет три параметра, но мы только что передали два(3 и 7). Точно так же для функции multi() требуется один аргумент, но при вызове функции obj.multi() не было передано ни одного аргумента. Так почему же это не дает ошибок?
Как использовать?
Когда мы вызываем метод экземпляра с некоторыми параметрами, корреляционная функция класса вызывается путем помещения объекта метода перед первым параметром. Итак, что-нибудь вроде obj.multi(args) становится Class.multi(obj, args). Вызывающий процесс является автоматическим, а принимающий процесс — нет(явный).
Это основная причина, по которой первым аргументом функции в классе должен быть сам объект. Запись этого параметра как self является чисто соглашением. Это не ключевое слово и не имеет особого значения в Python.
Мы могли бы использовать другие имена, но это крайне не рекомендуется. Использование имен, отличных от self, не одобряется большинством разработчиков и ухудшает читабельность кода.
Понятно, что сам объект(экземпляр) автоматически передается в качестве первого аргумента. Неявного поведения можно избежать при создании статического метода. Мы видели метод класса ранее в этом посте.
Явный self не уникален для Python. Эта идея была позаимствована у Modula-3.
Python __init__()
Метод Python __init__() не является конструктором. Многие программисты путаются с этим, поскольку __init__() вызывается, когда мы создаем объект.
Если вы внимательно посмотрите, то увидите, что первый параметр в __init__() — это сам объект(объект уже существует). Функция __init__() вызывается сразу после создания объекта и используется для его инициализации.
С технической точки зрения конструктор — это метод, создающий сам объект. В Python этот метод называется __new__(). Типичная сигнатура этого метода следующая.
|
1 |
__new__(cls, *args, **kwargs) |
При вызове метода __new__() класс автоматически передается в качестве первого аргумента(cls).
Опять же, как и self, cls — это просто соглашение об именах. Кроме того, *args и **kwargs используются для получения произвольного количества параметров во время вызовов функций в Python.
Некоторые важные вещи, которые следует помнить при реализации __new__():
- __new__() всегда вызывается перед __init__().
- Первый аргумент — это сам класс, который передается неявно.
- Всегда возвращайте действительный объект из __new__(). Не обязательно, но его основное использование — создание и возврат объекта.
См. следующий код.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class MyClass: def __new__(cls, *args, **kwargs): print("__NEW__") print(cls) print(args) print(kwargs) # create our object and return it obj = super().__new__(cls) return obj def __init__(self, a, b): self.a = a self.b = b def multi(self): return self.a * self.b obj = MyClass(3, 7) print('The multiplication of a and b is: ', obj.multi()) |
Выход:
|
1 2 3 4 |
__NEW__ <class '__main__.MyClass'>(3, 7) {} The multiplication of a and b is: 21 |
Этот пример показывает, что __new__() вызывается перед __init__(). Мы также можем видеть, что параметр cls в __new__() является самим классом(Point). Наконец, объект создается путем вызова метода __new__() в базовом классе объекта.
В Python объект — это базовый класс, от которого происходят все остальные классы. В приведенном выше коде мы сделали это с помощью super().
