4

I found when converting some Python2/Qt4 code to Python3/Qt5, that apparently QStandardItem can no longer be used as a dict key in as it doesn't have __hash__ implemented, and therefore is not considered immutable anymore.

Those two snippets show the issue:

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

Why was the change done? Should I not use QStandardItem as a dict key?

The obvious workaround would be to subclass QStandardItem and reimplement a trivial version of __hash__ (which I've done). But is there anything I'm missing?

4

1 回答 1

2

在 Qt 中,对 hashability 有三个要求

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

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

将 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

这正是我们想要的:比较相等的对象也应该散列相等。但是现在看看在 Python 2 中使用相同版本的 PyQt 时会发生什么:

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

似乎Python 2中的实现使用了对象标识__hash__之类的东西,这显然与 Qt 的散列语义不一致。

该类在 QtQStandardItem从来没有可散列,因此为了一致性,PyQt 现在选择不为其提供__hash__方法。而且由于实例QStandardItem实际上是可变的,PyQt 相当合理地让用户决定何时定义__hash__以及如何实现它。为了与 Python 2 兼容,这可能只是返回id(self).

于 2017-12-11T20:25:15.560 回答