【问题标题】:setting imported functions as members in a static dictionary将导入的函数设置为静态字典中的成员
【发布时间】:2019-05-09 18:31:56
【问题描述】:

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

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
    ...

只有前两个继续工作(直接设置函数),其他 - 使用函数做同样的事情 - 在所有 eval 调用上给出 NameError。例如:NameError: name 'user_func5' is not defined

使用方法与直接设置函数时范围损失的逻辑是什么?我是否可以使用从其他包中导入来使其工作,以便我可以使用方法而不是直接将任何函数放入类中?

【问题讨论】:

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


    【解决方案1】:

    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)

    ...如果两个字典[即全局变量和局部变量]都被省略,则表达式将在调用 eval() 的环境中执行。

    因此,可以合理地假设您遇到的问题归结为globalslocals 在上下文中的内容(即在ClassTest 的定义(可能是单独的模块)中) eval 被调用。由于调用eval 的上下文通常不是您定义和/或导入user_func, user_func2.... 的上下文,因此就eval 而言,这些函数是未定义的。这个思路得到了docs for globals的支持:

    全局变量()

    ...这始终是当前模块的字典(在函数或方法中,这是定义它的模块,而不是调用它的模块)。

    修复

    对于如何修复此代码,您有几种不同的选择。所有这些都将涉及将locals 从您调用的上下文(例如ClassTest.registerClassFunc)传递到定义该方法的上下文。此外,您应该借此机会从代码中排除eval 的使用(它的使用被认为是不好的做法,它是massive security hole,yadda yadda yadda)。鉴于locals 是定义user_func 的范围的字典,您总是可以这样做:

    locals['user_func'] 
    

    代替:

    eval('user_func')
    

    修复 #1

    Link to live version of this fix

    这将是最容易实现的修复,因为它只需要对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 定义,您编写的导入测试现在将按预期运行。

    优点

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

    • 不涉及对函数签名的更改。

    缺点

    修复 #2

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-30
      • 1970-01-01
      • 2018-09-18
      • 2021-09-10
      相关资源
      最近更新 更多