diff --git a/translations/README-ru.md b/translations/README-ru.md index e79facc..b5627b6 100644 --- a/translations/README-ru.md +++ b/translations/README-ru.md @@ -1075,3 +1075,105 @@ False * Более подробное объяснение можно найти [здесь] (https://www.naftaliharris.com/blog/python-subclass-intransitivity/). --- + + +### ▶ Равенство и тождество методов + + +1. +```py +class SomeClass: + def method(self): + pass + + @classmethod + def classm(cls): + pass + + @staticmethod + def staticm(): + pass +``` + +**Результат:** +```py +>>> print(SomeClass.method is SomeClass.method) +True +>>> print(SomeClass.classm is SomeClass.classm) +False +>>> print(SomeClass.classm == SomeClass.classm) +True +>>> print(SomeClass.staticm is SomeClass.staticm) +True +``` + +Обращаясь к `classm` дважды, мы получаем одинаковый объект, но не *тот же самый*? Давайте посмотрим, что происходит +с экземплярами `SomeClass`: + +2. +```py +o1 = SomeClass() +o2 = SomeClass() +``` + +**Вывод:** +```py +>>> print(o1.method == o2.method) +False +>>> print(o1.method == o1.method) +True +>>> print(o1.method is o1.method) +False +>>> print(o1.classm is o1.classm) +False +>>> print(o1.classm == o1.classm == o2.classm == SomeClass.classm) +True +>>> print(o1.staticm is o1.staticm is o2.staticm is SomeClass.staticm) +True +``` + +Повторный доступ к `классу` или `методу` создает одинаковые, но не *те же самые* объекты для одного и того же экземпляра `какого-либо класса`. + +#### 💡 Объяснение +* Функции являются [дескрипторами](https://docs.python.org/3/howto/descriptor.html). Всякий раз, когда к функции обращаются как к +атрибута, вызывается дескриптор, создавая объект метода, который "связывает" функцию с объектом, владеющим атрибутом. При вызове метод вызывает функцию, неявно передавая связанный объект в качестве первого аргумента +(именно так мы получаем `self` в качестве первого аргумента, несмотря на то, что не передаем его явно). +```py +>>> o1.method +> +``` +* При многократном обращении к атрибуту каждый раз создается объект метода! Поэтому `o1.method is o1.method` всегда ложно. Однако доступ к функциям как к атрибутам класса (в отличие от экземпляра) не создает методов; поэтому +`SomeClass.method is SomeClass.method` является истинным. +```py +>>> SomeClass.method + +``` +* `classmethod` преобразует функции в методы класса. Методы класса - это дескрипторы, которые при обращении к ним создают +объект метода, который связывает *класс* (тип) объекта, а не сам объект. +```py +>>> o1.classm +> +``` +* В отличие от функций, `classmethod` будет создавать метод и при обращении к нему как к атрибуту класса (в этом случае они +привязываются к классу, а не к его типу). Поэтому `SomeClass.classm is SomeClass.classm` является ошибочным. +```py +>>> SomeClass.classm +> +``` +* Объект-метод равен, если обе функции равны, а связанные объекты одинаковы. Поэтому +`o1.method == o1.method` является истинным, хотя и не является одним и тем же объектом в памяти. +* `staticmethod` преобразует функции в дескриптор "no-op", который возвращает функцию как есть. Методы-объекты +никогда не создается, поэтому сравнение с `is` является истинным. +```py +>>> o1.staticm + +>>> SomeClass.staticm + +``` +* Необходимость создавать новые объекты "метод" каждый раз, когда Python вызывает методы экземпляра, и необходимость изменять аргументы +каждый раз, чтобы вставить `self`, сильно сказывается на производительности. +CPython 3.7 [решил эту проблему](https://bugs.python.org/issue26110), введя новые опкоды, которые работают с вызовом методов +без создания временных объектов методов. Это используется только при фактическом вызове функции доступа, так что +приведенные здесь фрагменты не затронуты и по-прежнему генерируют методы :) + +---