有一个简单的类,我想使用不同的方式在字典中静态存储一些函数:
import os, sys
class ClassTest():
testFunc = {}
def registerClassFunc(self,funcName):
ClassTest.testFunc[funcName] = eval(funcName)
@classmethod
def registerClassFuncOnClass(cls,funcName):
cls.testFunc[funcName] = eval(funcName)
@staticmethod
def registerClassFuncFromStatic(funcName):
ClassTest.testFunc[funcName] = eval(funcName)
一些示例方法:
def user_func():
print("I run therefore I am self-consistent")
def user_func2():
print("I am read therefore I am interpreted")
def user_func3():
print("I am registered through a meta function therefore I am not recognized")
def user_func4():
print("I am registered through an instance function therefore I am not recognized")
def user_func5():
print("I am registered through a static function therefore I am not recognized")
还有一点测试:
if __name__ == "__main__":
a = ClassTest()
a.testFunc["user_func"] = user_func
a.testFunc["user_func"]()
a.testFunc["user_func2"] = eval("user_func2")
a.testFunc["user_func2"]()
ClassTest.testFunc["user_func"] = user_func
ClassTest.testFunc["user_func"]()
ClassTest.testFunc["user_func2"] = eval("user_func2")
ClassTest.testFunc["user_func2"]()
a.registerClassFunc("user_func5") # does not work on import
a.testFunc["user_func5"]()
ClassTest.registerClassFuncFromStatic("user_func3") # does not work on import
ClassTest.testFunc["user_func3"]()
ClassTest.registerClassFuncOnClass("user_func4") # does not work on import
ClassTest.testFunc["user_func4"]()
提供的所有这些方法都在同一个文件中。一旦将功能拆分为2个文件和一个主文件:
from ClassTest import ClassTest
from UserFunctions import user_func,user_func2, user_func3, user_func4, user_func5
if __name__ == "__main__":
a = ClassTest()
a.testFunc["user_func"] = user_func
...
只有前两个保持工作(直接设置函数),其他两个(使用函数执行相同的操作)在所有NameError
调用中赋予eval
。例如:NameError: name 'user_func5' is not defined
。
在使用方法而不是直接设置功能时,失去范围的逻辑是什么?而且我可以使用其他包中的导入功能使它正常工作,以便可以使用方法而不是直接将任何函数放置在类中吗?
答案 0 :(得分:2)
There's a live version of fix #1 from this answer online that you can try out for yourself
您说对了,这不起作用的原因是范围界定问题。您可以通过仔细检查docs for eval
来了解发生了什么:
eval(表达式,globals = None,locals = None)
...如果省略了两个字典[globals和locals],则在调用eval()的环境中执行该表达式。
因此,可以合理地假设,您遇到的问题取决于{{1的定义(可能是单独的模块)内)globals
和locals
的内容{1}}),其中调用ClassTest
。通常,由于调用eval
的上下文不是您定义和/或导入eval
的上下文,因此就user_func, user_func2....
而言,这些函数是未定义的。 docs for globals
支持这一思路:
globals()
...这始终是当前模块的字典(在函数或方法内部,这是定义它的模块,而不是从中调用它的模块)。
关于如何修复此代码,您有几种不同的选择。所有这些都将涉及将eval
从您调用的上下文(例如locals
)传递到定义该方法的上下文。此外,您应该借此机会从代码中排除ClassTest.registerClassFunc
的使用(它的使用被认为是不好的做法,这是massive security hole,yadda yadda yadda)。假设eval
是定义locals
的作用域的决定,那么您始终可以执行以下操作:
user_func
而不是:
locals['user_func']
Link to live version of this fix
这将是最容易实现的修复程序,因为它只需要对eval('user_func')
的方法定义进行一些调整(无需更改任何方法签名)。它依赖于以下事实:可以在函数中使用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
仅保证可在CPython上运行。 Mileage may vary when running this code with other implementations of Python。
修复#2与修复#1基本相同,除了在此版本中,在调用点将inspect.currentframe()
显式传递给locals
的方法。例如,在此修复程序下,ClassTest
的定义为:
ClassTest.registerClassFunc
,您将在代码中这样调用它:
def registerClassFunc(self, funcName, _locals):
ClassTest.testFunc[funcName] = dictsGet(funcName, _locals, locals(), globals())
a = ClassTest()
a.registerClassFunc("user_func5", locals())
,因此它可能比Fix#1更具性能/便携性。您必须修改方法签名,因此还必须更改任何使用这些方法的现有代码。
您必须从此处开始将inspect.currentframe()
样板添加到每个locals()
方法的每次调用中。