优化`in`

时间:2011-12-28 11:19:11

标签: python optimization python-3.x pyqt pyqt4

我正在使用这样的结构来测试是否按下了所需的键:

def eventFilter(self, tableView, event):
    if event.type() == QtCore.QEvent.KeyPress:
        key = event.key()
        if event.modifiers() in (QtCore.Qt.NoModifier, QtCore.Qt.KeypadModifier):
            if key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return):
                self.menu.editItem.trigger()
                return True

我知道'过早优化是所有邪恶的根源',但我认为eventFilter经常被调用以考虑其优化。

我的担忧:

    每次运行时
  1. if key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return)进行双重查找:1。在Qt模块中查找QtCore属性; 2.在Key_Enter模块中查找Qt属性。
  2. if key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return)这会在每次运行时构造元组。元组中的搜索是顺序的 - 更好地使用frozenset
  3. 你如何处理此类案件?不在乎?

4 个答案:

答案 0 :(得分:3)

您的代码:

def eventFilter(self, tableView, event): 
    if event.type() == QtCore.QEvent.KeyPress: 
        key = event.key() 
        if event.modifiers() in (QtCore.Qt.NoModifier, QtCore.Qt.KeypadModifier): 
            if key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return): 
                self.menu.editItem.trigger() 
                return True

正如我在@interjay的评论中提到的,对于任何类型的UI事件,可能会有大量的函数调用,如果你有许多这样的过滤器,它们可能会导致UI缓慢。如果您希望至少对第一个if测试进行优化,那么将QtCore.QEvent.KeyPress的本地定义移动到默认参数值:

def eventFilter(self, tableView, event,
    FILTER_EVENT_TYPE=QtCore.QEvent.KeyPress
    ): 
    if event.type() == FILTER_EVENT_TYPE: 
        if event.modifiers() in (QtCore.Qt.NoModifier, QtCore.Qt.KeypadModifier): 
            key = event.key() 
            if key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return): 
                self.menu.editItem.trigger() 
                return True

(在event.modifiers()上进行测试后,我还将函数调用event.key()移动到。)

这样的默认参数在导入模块时在函数编译时一次评估,而不是每次调用一次,因此QtCore.QEvent.KeyPress 的查找将是加快。你当然可以把它带到极端:

def eventFilter(self, tableView, event,
    FILTER_EVENT_TYPE=QtCore.QEvent.KeyPress
    FILTER_MODIFIERS=(QtCore.Qt.NoModifier, QtCore.Qt.KeypadModifier),
    FILTER_KEYS=(QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return)
    ): 
    if (event.type() == FILTER_EVENT_TYPE and
        event.modifiers() in FILTER_MODIFIERS and 
        event.key() in FILTER_KEYS): 
            self.menu.editItem.trigger() 
            return True

现在你不仅优化了模块 - 对象 - 属性查找,而且还优化了元组结构,并且正如@AndrewDalke所提到的,我对in的测试表明它对元组的速度比设置大小更快。约3或4个元素。当条件的任何部分失败时,单个条件仍然会短路,因此如果类型不是按键,则不会调用event.modifiers或event.key。

编辑:我喜欢@ ekhumoro对键和修饰符的耦合测试,以下是它看起来如何合并到我的代码中:

def eventFilter(self, tableView, event,
    FILTER_EVENT_TYPE=QtCore.QEvent.KeyPress
    FILTER_KEY_MODIFIERS=((QtCore.Qt.Key_Return, QtCore.Qt.NoModifier),
                          (QtCore.Qt.Key_Enter, QtCore.Qt.KeypadModifier),
                          )
    ): 
    if (event.type() == FILTER_EVENT_TYPE and
        (event.key(), event.modifiers()) in FILTER_KEY_MODIFIERS): 
            self.menu.editItem.trigger() 
            return True

答案 1 :(得分:2)

我赞同在这种情况下无关紧要的评论。

但是如果你想在这里节省时间,那么在Python中删除重复查找成本的规范方法是将对象缓存在局部变量名称空间中:

NoModifier = QtCore.Qt.NoModifier
KeypadModifier = QtCore.Qt.KeypadModifier
if event.modifiers() in (NoModifier, KeypadModifier):
    ...

Python将在局部变量命名空间,模块命名空间中查找变量,最后在全局命名空间中查找 - 您可以通过将内容放入局部变量命名空间来赢得某些东西。

但是,在您的情况下,没有意义:每个函数调用一次完成查找。如果您有一个循环,您可以执行许多相同属性的查找,则上述优化策略适用:

for event in huge_pile_of_accumulated_events:
    if event.modifiers() in (NoModifier, KeypadModifier):
        ...

在那里你可能会保存一些东西 - 记得首先分析你的代码以真正证明这很重要!对于每个键盘事件运行一次的处理程序,查找时间不重要。

答案 2 :(得分:1)

虽然多个属性查找确实会更慢,但由于我们每次查询的时间不到百万分之一秒,所以这些差异会被其他更大的因素(例如简单调用的成本)所淹没一个方法)。所以这比无意义优化更像是过早优化。

但是,如果你真的很关心它,只需更改你的import语句就可以避免很多查找。

所以不要这样做:

from PyQt4 import QtCore

你可以这样做:

from PyQt4.QtCore import QEvent

并避免对模块中的每个 QEvent引用进行额外的属性查找(当然,对于以这种方式导入的任何其他类,也是如此)。

就个人而言,我也会避免所有in测试,而是单独测试每种可能性。这样可以节省每次测试运行时创建元组的成本,并且还可以利用短路评估。

所以我会将你的示例代码重写为:

from PyQt4.QtCore import Qt, QEvent

def eventFilter(self, tableView, event):
    if event.type() == QEvent.KeyPress:
        key = event.key()
        modifiers = event.modifiers()
        if ((modifiers == Qt.NoModifier and key == Qt.Key_Return) or 
            (modifiers == Qt.KeypadModifier and key == Qt.Key_Enter)):
            self.menu.editItem.trigger()
            return True

答案 3 :(得分:0)

回应“不要打扰”。但是你应该注意一些事情。

首先,最重要的是,函数中的元组文字在构造函数本身时构造为一次,并存储为常量。所以你没有得到你要求的案例 - 不断重建元组(和随附的查找)。

其次,将QtCore.QEvent.KeyPress绑定到模块级全局使您受益(少量)。目前,您有QtCore的模块级查找,然后是QEvent的模块级查找,接下来(我认为)是KeyPress的模块级查找(可能是类级别,但大多数类的成本相似)。做类似的事情:

QtKeyPress = QtCore.QEvent.KeyPress

from QtCore.QEvent import KeyPress as QtKeyPress

您将把它减少到单个模块级查找。您可以使用默认参数技巧将其缩减为本地查找,但这也有缺点 - 代码的丑陋以及有人在函数调用时覆盖绑定的可能性。