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

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


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。


使用方法与直接设置功能时失去范围的逻辑是什么?我可以使用来自其他包的导入来让它工作,这样我就可以使用方法而不是直接在类中放置任何函数吗?


哆啦的时光机
浏览 152回答 1
1回答

白猪掌柜的

问题你是对的,这不起作用的原因是由于范围问题。您可以通过检查以下文档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 inspectdef 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 的其他实现一起运行时,里程可能会有所不同。修复 #2Fix #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方法的每次调用中。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript