解决Python中abc.Sequence、abc.Hashable、list之间的继承矛盾

Posted

技术标签:

【中文标题】解决Python中abc.Sequence、abc.Hashable、list之间的继承矛盾【英文标题】:Solving inheritance contradictions among abc.Sequence, abc.Hashable and list in Python 【发布时间】:2019-09-23 15:50:20 【问题描述】:

我使用的是 3.6.3 版

我正在研究Pythoncollections.abc的类间继承关系。我在listSequenceHashable之间发现了一些矛盾的继承关系

如您所知, 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(意味着'你不能'hash(some_list)')

issubclass(list, Hashable)


False

我认为这种继承是矛盾的。我完全理解list 是可变的,因为我已经使用了数百次list,但是继承关系图不支持这个概念。我错了什么或遗漏了什么?

我等待您的建议。谢谢。

【问题讨论】:

你看过这个博客吗:naftaliharris.com/blog/python-subclass-intransitivity?它看起来很不错,这里引用了一句,“...Iterable 是 Hashable 的“子类”——说真的,因为 Iterable 具有从对象继承的 __hash__ 方法”。 Sequence 有一个__hash__ 方法,但是根据docs.python.org/3.6/library/…,Sequence 不继承自Hashable。 (不是答案,只是观察。) @SuperShoot 感谢您的参考。我非常喜欢这种方法:在 Python 中获取所有类并测试所有三元组依赖项检查。我想我应该稍后再深入研究metaclass。 :) 我也将那本书添加到我的阅读清单中。感谢您的出色 Q 并祝您在 Python 学习中一切顺利。 【参考方案1】:

所以它实际上是一个有趣的设计特性,但 Python 子类实际上不需要是可传递的。 Google 定义的传递性:

"如果一个特征适用于序列的连续成员之间,它也必须适用于按顺序排列的任何两个成员之间。例如,如果 A 大于 B,并且 B 大于 C,则 A 大于C。”

对于继承是传递性的语言,例如Java,如果B继承AC继承B,那么C必须继承AC 的传递超类集为ABObject,直接超类为B

在 Python 中,我们背离了这个概念。正如您所指出的,SequenceHashablelistSequence,但 list 不是 Hashable。事实上,list 并没有直接继承任何东西(除了 object 每个 python 类都继承)

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

在 Python 中,您可以使用元类实用程序中的 __subclasscheck____subclasshook__ 来使内置方法 issubclass 做一些有趣的事情。元类是一种高级语言功能,用于修改类操作的基本规则(修改issubclass 的工作方式就是一个很好的例子)。在抽象基类元类ABCMeta 中,__subclasscheck__ 方法将在类上调用__subclasshook__ 方法(如果已定义)。您可以阅读great answer about the uses here。

某些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

【讨论】:

class list(object): 不会出现在 list 类的实际定义中的任何位置。 class list(object) 出现在 help(list) 输出中,它可能出现在 PyCharm 假源代码或类似的东西中,但 actual class definition 在 C 中。 @user2357112 啊,谢谢,好点,我会把它加进去。虽然列表只继承对象,但它仍然是正确的;由于是一个 python 对象。 谢谢我的朋友。非常有趣的是 Python 类并不简单地遵循概念继承树。但是你能告诉我transitive的含义吗?我不会说英语,所以这让我有点困惑。 @flakes 谢谢,您编辑的答案更清晰。但它也给我带来了更多的问题。为什么你的代码实现subclasshook,而不是subclasscheck,什么是_check_methods?我明白了,很感激。 @flakes 请知道,我非常感谢您为做出无错误和更清晰的答案所做的努力。我的 Python 得到了改进。非常感谢:)

以上是关于解决Python中abc.Sequence、abc.Hashable、list之间的继承矛盾的主要内容,如果未能解决你的问题,请参考以下文章

python定义接口继承类invalid syntax解决办法

在MySQL中出现Unknown column 'abc' in 'field list'怎么解决?

python中len('abc')输出?

如何在 Python 中连接变量加上 os.environ 路径和 yaml

Python:子类整个层次结构

Python中abc