使用exec执行含类继承的PyQt代码片段时出现作用域相关报错
问题原因及修复方案
问题根源
报错完全由exec()的作用域规则导致:
- 全局作用域调用
exec()时,执行产生的所有类、变量都会存入全局命名空间,Test类__init__方法调用super(Test, self)时可以正常在全局作用域找到Test类定义,运行正常。 - 在
keyPressEvent实例方法的局部作用域调用exec()时,默认会把执行产生的所有名称存入当前方法的局部命名空间(locals()),而Python类内部方法运行时不会向上查找外层函数的局部作用域,因此__init__里的super(Test, self)找不到Test类定义,触发NameError。多次执行时局部命名空间残留的旧类定义还会导致类类型不匹配,触发super()的TypeError。
修复方案
只需要修改keyPressEvent中的exec调用语句,主动指定执行的全局命名空间即可,两种可选方案:
方案1:直接使用主程序全局命名空间
和全局作用域执行效果完全一致,修改代码为:
exec(self.toPlainText(), globals())
方案2:使用独立命名空间(推荐,避免污染主程序变量)
如果不想让执行代码的变量覆盖主程序的全局变量,可创建独立字典作为执行上下文的全局命名空间:
# 创建独立的执行上下文,所有执行产生的变量都存在该字典中 exec_context = {} exec(self.toPlainText(), exec_context)
完整修改后的keyPressEvent代码示例
def keyPressEvent(self, event): if event.modifiers() == Qt.ControlModifier: if event.key() == Qt.Key_Return: # 两种方案二选一即可 # 方案1 exec(self.toPlainText(), globals()) # 方案2 # exec_context = {} # exec(self.toPlainText(), exec_context) return QPlainTextEdit.keyPressEvent(self, event)
注:原来注释的
exec(compile(self.toPlainText(), "script", "exec"), globals(), locals())同样会报错,原因是显式指定了局部命名空间为locals(),类定义还是会被存入局部作用域。
内容的提问来源于stack exchange,提问作者Emmuh
相关产品推荐
相关产品推荐

