Translate The mysterious key type conversion example

This commit is contained in:
Vadim Nifadev 2024-04-22 15:08:54 +03:00
parent 58de881f93
commit f96f5117c5
1 changed files with 57 additions and 0 deletions

View File

@ -1806,3 +1806,60 @@ NameError: name 'e' is not defined
```
---
### ▶ Загадочное преобразование типов ключей
<!-- Example ID: 00f42dd0-b9ef-408d-9e39-1bc209ce3f36 --->
```py
class SomeClass(str):
pass
some_dict = {'s': 42}
```
**Результат:**
```py
>>> type(list(some_dict.keys())[0])
str
>>> s = SomeClass('s')
>>> some_dict[s] = 40
>>> some_dict # Ожидается 2 разные пары ключ-значение
{'s': 40}
>>> type(list(some_dict.keys())[0])
str
```
#### 💡 Объяснение:
* И объект `s`, и строка `"s"` хэшируются до одного и того же значения, потому что `SomeClass` наследует метод `__hash__` класса `str`.
* Выражение `SomeClass("s") == "s"` эквивалентно `True`, потому что `SomeClass` также наследует метод `__eq__` класса `str`.
* Поскольку оба объекта хэшируются на одно и то же значение и равны, они представлены одним и тем же ключом в словаре.
* Чтобы добиться желаемого поведения, мы можем переопределить метод `__eq__` в `SomeClass`.
```py
class SomeClass(str):
def __eq__(self, other):
return (
type(self) is SomeClass
and type(other) is SomeClass
and super().__eq__(other)
)
# При переопределении метода __eq__, Python прекращает автоматическое наследование метода
# __hash__, поэтому его нужно вручную определить
__hash__ = str.__hash__
some_dict = {'s':42}
```
**Результат:**
```py
>>> s = SomeClass('s')
>>> some_dict[s] = 40
>>> some_dict
{'s': 40, 's': 42}
>>> keys = list(some_dict.keys())
>>> type(keys[0]), type(keys[1])
(__main__.SomeClass, str)
```
---