为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

哆啦的时光机 2021-09-11 13:23:39
有一个简单的类,我想使用不同的方式在字典中静态存储一些函数:import os, sysclass 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 ClassTestfrom UserFunctions import user_func,user_func2, user_func3, user_func4, user_func5if __name__ == "__main__":    a = ClassTest()    a.testFunc["user_func"] = user_func    ...只有前两个继续工作(直接设置函数),其他的 - 使用一个函数来做同样的事情 -NameError在所有eval调用上给出一个。例如:NameError: name 'user_func5' is not defined。使用方法与直接设置功能时失去范围的逻辑是什么?我可以使用来自其他包的导入来让它工作,这样我就可以使用方法而不是直接在类中放置任何函数吗?
查看完整描述

1 回答

?
白猪掌柜的

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方法的每次调用中。


查看完整回答
反对 回复 2021-09-11
  • 1 回答
  • 0 关注
  • 151 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信