1 回答
![?](http://img1.sycdn.imooc.com/54584de700017cbd02200220-100-100.jpg)
TA贡献1893条经验 获得超10个赞
问题
你是对的,这不起作用的原因是由于范围问题。您可以通过检查以下文档eval来弄清楚发生了什么:
评估(表达式,全局=无,本地=无)
...如果两个字典 [即全局变量和局部变量] 都被省略,则表达式在调用 eval() 的环境中执行。
因此,可以合理地假设您遇到的问题归结为被调用的内容globals和locals上下文(即在 的定义(和可能的单独模块)内ClassTest)eval。由于eval被调用的上下文通常不是您定义和/或导入的上下文,因此就相关而言user_func, user_func2....,这些函数是未定义的eval。这一思路得到以下文档的支持globals:
全局变量()
...这始终是当前模块的字典(在函数或方法中,这是定义它的模块,而不是调用它的模块)。
修复
对于如何修复此代码,您有几种不同的选择。所有这些都将涉及locals从您调用的上下文传递,例如,ClassTest.registerClassFunc传递到定义该方法的上下文。此外,您应该借此机会eval从您的代码中排除使用(它的使用被认为是不好的做法,它是一个巨大的安全漏洞,yadda yadda yadda)。鉴于locals这user_func是定义的范围的字典,您可以随时执行以下操作:
locals['user_func']
代替:
eval('user_func')
修复 #1
链接到此修复程序的实时版本
这将是最容易实现的修复,因为它只需要对 的方法定义进行一些调整ClassTest(并且不需要更改任何方法签名)。它依赖于可以inspect在函数中使用包直接获取locals调用上下文的事实:
import inspect
def dictsGet(s, *ds):
for d in ds:
if s in d:
return d[s]
# if s is not found in any of the dicts d, treat it as an undefined symbol
raise NameError("name %s is not defined" % s)
class ClassTest():
testFunc = {}
def registerClassFunc(self, funcName):
_frame = inspect.currentframe()
try:
_locals = _frame.f_back.f_locals
finally:
del _frame
ClassTest.testFunc[funcName] = dictsGet(funcName, _locals, locals(), globals())
@classmethod
def registerClassFuncOnClass(cls, funcName):
_frame = inspect.currentframe()
try:
_locals = _frame.f_back.f_locals
finally:
del _frame
cls.testFunc[funcName] = dictsGet(funcName, _locals, locals(), globals())
@staticmethod
def registerClassFuncFromStatic(funcName):
_frame = inspect.currentframe()
try:
_locals = _frame.f_back.f_locals
finally:
del _frame
ClassTest.testFunc[funcName] = dictsGet(funcName, _locals, locals(), globals())
如果您使用上面给出的 定义ClassTest,您编写的导入测试现在将按预期运行。
优点
完全提供最初预期的功能。
不涉及对函数签名的更改。
缺点
调用inspect.currentframe()可能会导致性能下降,因此如果您计划ClassTest每秒调用一百万次方法,则可能无法使用此修复程序。
inspect.currentframe()只能保证在 CPython 上工作。将此代码与 Python 的其他实现一起运行时,里程可能会有所不同。
修复 #2
Fix #2 与 fix #1 基本相同,除了在此版本中您在调用点显式传递locals到 的方法ClassTest。例如,在此修复下, 的定义ClassTest.registerClassFunc将是:
def registerClassFunc(self, funcName, _locals):
ClassTest.testFunc[funcName] = dictsGet(funcName, _locals, locals(), globals())
你会像这样在你的代码中调用它:
a = ClassTest()
a.registerClassFunc("user_func5", locals())
优点
不依赖于inspect.currentframe(),因此可能比修复 #1 更具性能/便携性。
缺点
您必须修改方法签名,因此您还必须更改使用这些方法的任何现有代码。
从这里开始,您必须将locals()样板添加到每个ClassTest方法的每次调用中。
添加回答
举报