[英]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
经常被调用以考虑其优化。
我的担忧:
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
属性。 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.