繁体   English   中英

优化`在`

[英]Optimizing `in`

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

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)每次运行时if key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return)进行两次查找:1.在QtCore模块中找到Qt属性; 2.在Qt模块中找到Key_Enter属性。
  2. if key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return) ,则每次运行时都会构造元组。 元组中的搜索是顺序的-更好地使用frozenset吗?

您如何处理这种情况? 不在乎吗

您的代码:

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

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

但是,如果您想在此处节省时间,那么消除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):
        ...

您可能在那里保存了一些东西-记住首先分析您的代码以实际证明这很重要! 对于每个键盘事件运行一次的处理程序,查找时间将无关紧要。

的确,多重属性查找的速度会变慢,因为我们正在谈论的是每次查找的时间不到一分之一秒,但这种差异将被其他更大的因素(例如,简单地调用方法的成本)所淹没。 。 因此,与过早优化相比,这更是无意义优化的情况。

但是,如果您真的很担心,只需更改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

回应“别打扰”。 但是,您需要注意几件事。

首先,也是最重要的一点是,在函数本身被编译并存储为常量之后 ,函数中的元组文字将被构造一次 因此,您没有所要询问的情况-不断重建元组(以及伴随的查找)。

其次,结合QtCore.QEvent.KeyPress到一个模块级的全球有利于你(少量)。 当前,您有一个针对QtCore的模块级查找,其次是针对QEvent的模块级查找,其次(我认为)是针对KeyPress的一个模块级查找(它可能是类级的,但是在大多数类中这是相似的代价) 。 通过执行以下操作:

QtKeyPress = QtCore.QEvent.KeyPress

要么

from QtCore.QEvent import KeyPress as QtKeyPress

您可以将其简化为单个模块级查找。 您可以使用默认参数技巧将其简化为本地查找,但这也有缺点-代码难看,以及在函数调用时有人重写绑定的可能性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM