将导入的函数设置为静态字典中的成员

时间:2018-12-08 02:03:29

标签: python python-3.x function python-import python-object

有一个简单的类,我想使用不同的方式在字典中静态存储一些函数:

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

在使用方法而不是直接设置功能时,失去范围的逻辑是什么?而且我可以使用其他包中的导入功能使它正常工作,以便可以使用方法而不是直接将任何函数放置在类中吗?

1 个答案:

答案 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的定义(可能是单独的模块)内)globalslocals的内容{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'] 

修复#1

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()) 定义,则准备好的导入测试现在将按预期运行。

专业人士

  • 完全提供最初预期的功能。

  • 不更改功能签名。

缺点

修复#2

修复#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()方法的每次调用中。