Разрешение противоречий наследования среди abc.Sequence, abc.Hashable и списка в Python - PullRequest
5 голосов
/ 06 мая 2019

Я использую версию 3.6.3

Я изучаю отношения наследования Python collections.abc между классами. И я нашел некоторые противоречивые наследства между list, Sequence и Hashable

Как вы уже знаете,
1. Sequence наследует Hashable класс и
2. list наследует Sequence

from collections import Sequence, Hashable


issubclass(Sequence, Hashable)  # 1.
issubclass(list, Sequence)      # 2.

True
True

Исходя из этого, как вы можете подумать, list также наследует Hashable концептуально.

Но list является изменяемым и не наследует Hashable (означает «вы не можете» хэш (some_list) ')

issubclass(list, Hashable)


False

Я думаю, что это наследство противоречиво. Я полностью понимаю, что list является изменчивым, потому что я использовал list сотни раз, но график отношений наследования не поддерживает эту концепцию. Что я не прав или отсутствует?

Я жду твоего возвращения. Спасибо.

1 Ответ

3 голосов
/ 06 мая 2019

Так что это действительно интересная особенность дизайна, но подклассы Python на самом деле не должны быть транзитивными. Переходный, как определено Google:

"если признак применим между последовательными членами последовательности, он также должен применяться между любыми двумя членами, взятыми по порядку. Например, если A больше, чем B, а B больше, чем C, то A больше, чем C.»

Для языков, где наследование является транзитивным, например Java, если B наследует A и C наследует B, тогда C должен наследовать A. Набор транзитивных суперклассов для C будет A, B и Object, а прямой суперкласс - B.

В Python мы отступаем от этой концепции. Как вы указали, Sequence - это Hashable, а list - это Sequence, но list - это не Hashable. На самом деле, список напрямую не наследует ничего (кроме object, которое наследует каждый класс python) .

# from the PyCharm generated stubs
class list(object):
    ...

В Python вы можете использовать __subclasscheck__ или __subclasshook__ из утилит метакласса, чтобы встроенный метод issubclass делал интересные вещи. Мета-класс - это расширенная языковая функция, используемая для изменения основных правил работы класса (отличный пример работы issubclass) . В абстрактном метаклассе базового класса ABCMeta метод __subclasscheck__ вызовет метод __subclasshook__ для класса, если он определен. Вы можете прочитать отличный ответ об использовании здесь .

Некоторые ABCMeta классы, такие как Hashable, реализуют __subclasshook__, чтобы не проверять дерево наследования, а проверять наличие метода. Это полезно, так как общие контракты не нужно включать в каждое определение класса, которое вы делаете.

Для этого случая, чтобы быть Hashable, необходимо определить __hash__. Тем не менее, Python заявляет, что не должен хешировать в List, потому что он изменчив, и поэтому он специально исключен из этого класса.

class Hashable(metaclass=ABCMeta):

    __slots__ = ()

    @abstractmethod
    def __hash__(self):
        return 0

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Hashable:
            return _check_methods(C, "__hash__")
        return NotImplemented

class list(object):
    ...
    __hash__ = None
...