QStandardItem 缺少 __hash__ 方法

QStandardItem missing __hash__ method

我在将一些 Python2/Qt4 代码转换为 Python3/Qt5 时发现,显然 QStandardItem 不能再用作字典键,因为它没有实现 __hash__,并且因此不再被认为是不可变的。

这两个片段显示了问题:

PyQt4:

>>> from PyQt4 import QtGui
>>> a = QtGui.QStandardItem()
>>> b = {}
>>> b[a] = "1"
>>> a.__hash__()
2100390

PyQt5:

>>> from PyQt5 import QtGui
>>> a = QtGui.QStandardItem()
>>> b = {}
>>> b[a] = "1"
TypeError: unhashable type: 'QStandardItem'
>>> a.__hash__()
TypeError: 'NoneType' object is not callable

为什么要进行更改?我不应该使用 QStandardItem 作为字典键吗?

明显的解决方法是子类化 QStandardItem 并重新实现 __hash__ 的简单版本(我已经完成了)。但是有什么我想念的吗?

在Qt中,一共有三种requirements for hashability

  1. 类型必须是可分配的数据类型
  2. 该类型必须定义一个 == 运算符
  3. 必须为类型
  4. 定义一个qHash函数

因此,如果 PyQt 想要与 Qt 保持一致,它应该只在上述条件适用时定义 __hash__,并且它的实现应该简单地委托给 Qt 提供的任何 qHash 函数。

使用 Python 2 和 PyQt4/5 时的行为可能应该被认为是一个错误特征,因为它给出的结果 与 Qt 一致。这可以通过查看 可散列的类型(在 Qt 术语中)会发生什么来看出:

使用 Python 3:

>>> a = QtCore.QUrl('foo.bar')
>>> b = QtCore.QUrl('foo.bar')
>>> a == b
True
>>> hash(a) == hash(b)
True

这正是我们想要的:compare 相等的对象,也应该 hash 相等。但是现在看看当使用相同版本的 PyQt 和 Python 2:

时会发生什么
>>> a = Qt.QUrl('foo.bar')
>>> b = Qt.QUrl('foo.bar')
>>> a == b
True
>>> hash(a) == hash(b)
False

Python 2 中的 __hash__ 实现似乎使用了对象的 身份 之类的东西,这显然永远不会与Qt 的哈希语义。

QStandardItemclass从未在 Qt 中可哈希,因此为了保持一致性,PyQt 现在选择不提供 __hash__ 方法。由于 QStandardItem 的实例实际上是可变的,因此 PyQt 相当合理地让用户决定何时定义 __hash__ 以及如何实现它。为了与 Python 2 兼容,这可能只是 return id(self).