Translate Methods equality and identity example

This commit is contained in:
Vadim Nifadev 2024-04-21 10:57:44 +03:00
parent a32eaca58f
commit 6e246d1487
1 changed files with 102 additions and 0 deletions

View File

@ -1075,3 +1075,105 @@ False
* Более подробное объяснение можно найти [здесь] (https://www.naftaliharris.com/blog/python-subclass-intransitivity/).
---
### ▶ Равенство и тождество методов
<!-- Example ID: 94802911-48fe-4242-defa-728ae893fa32 --->
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
<bound method SomeClass.method of <__main__.SomeClass object at ...>>
```
* При многократном обращении к атрибуту каждый раз создается объект метода! Поэтому `o1.method is o1.method` всегда ложно. Однако доступ к функциям как к атрибутам класса (в отличие от экземпляра) не создает методов; поэтому
`SomeClass.method is SomeClass.method` является истинным.
```py
>>> SomeClass.method
<function SomeClass.method at ...>
```
* `classmethod` преобразует функции в методы класса. Методы класса - это дескрипторы, которые при обращении к ним создают
объект метода, который связывает *класс* (тип) объекта, а не сам объект.
```py
>>> o1.classm
<bound method SomeClass.classm of <class '__main__.SomeClass'>>
```
* В отличие от функций, `classmethod` будет создавать метод и при обращении к нему как к атрибуту класса (в этом случае они
привязываются к классу, а не к его типу). Поэтому `SomeClass.classm is SomeClass.classm` является ошибочным.
```py
>>> SomeClass.classm
<bound method SomeClass.classm of <class '__main__.SomeClass'>>
```
* Объект-метод равен, если обе функции равны, а связанные объекты одинаковы. Поэтому
`o1.method == o1.method` является истинным, хотя и не является одним и тем же объектом в памяти.
* `staticmethod` преобразует функции в дескриптор "no-op", который возвращает функцию как есть. Методы-объекты
никогда не создается, поэтому сравнение с `is` является истинным.
```py
>>> o1.staticm
<function SomeClass.staticm at ...>
>>> SomeClass.staticm
<function SomeClass.staticm at ...>
```
* Необходимость создавать новые объекты "метод" каждый раз, когда Python вызывает методы экземпляра, и необходимость изменять аргументы
каждый раз, чтобы вставить `self`, сильно сказывается на производительности.
CPython 3.7 [решил эту проблему](https://bugs.python.org/issue26110), введя новые опкоды, которые работают с вызовом методов
без создания временных объектов методов. Это используется только при фактическом вызове функции доступа, так что
приведенные здесь фрагменты не затронуты и по-прежнему генерируют методы :)
---