Так что это действительно интересная особенность дизайна, но подклассы 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