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

2024-04-27 18:09:27 发布

您现在位置:Python中文网/ 问答频道 /正文

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

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"]()

所有这些工作都提供了所有这些元素都在同一个文件中。一旦功能被分成两个文件和一个主文件:

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。你知道吗

使用这些方法与直接设置这些函数相比,在这里丢失作用域的逻辑是什么?我是否可以使用从其他包导入的方法使它工作,这样我就可以用方法而不是直接在类中放置任何函数?你知道吗


Tags: 方法函数importdefevalnotamfunc
1条回答
网友
1楼 · 发布于 2024-04-27 18:09:27

There's a live version of fix #1 from this answer online that you can try out for yourself

问题

你是对的,这不起作用的原因是由于范围问题。您可以通过仔细检查docs for ^{}来了解发生了什么:

eval(expression, globals=None, locals=None)

...If both dictionaries [ie globals and locals] are omitted, the expression is executed in the environment where eval() is called.

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

globals()

...This is always the dictionary of the current module (inside a function or method, this is the module where it is defined, not the module from which it is called).

修复

对于如何修复此代码,您有几个不同的选项。所有这些都将涉及从调用的上下文传递locals,例如,ClassTest.registerClassFunc到定义该方法的上下文。此外,您应该利用这个机会从代码中排除eval的使用(它的使用被认为是不好的做法,它是massive security hole,yadda yadda yadda)。假定locals是定义user_func的作用域的dict,您可以始终执行以下操作:

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

Fix#2与Fix#1基本相同,只是在这个版本中,您在调用点显式地将locals传递给ClassTest的方法。例如,在这个fix下ClassTest.registerClassFunc的定义是:

def registerClassFunc(self, funcName, _locals):
        ClassTest.testFunc[funcName] = dictsGet(funcName, _locals, locals(), globals())

你可以在代码中这样调用它:

a = ClassTest()
a.registerClassFunc("user_func5", locals())

优点

  • 不依赖于inspect.currentframe(),因此可能比fix#1更具性能/可移植性。你知道吗

缺点

  • 您必须修改方法签名,因此还必须更改使用这些方法的任何现有代码。

  • 从现在开始,您必须将locals()样板添加到每个ClassTest方法的每次调用中。

相关问题 更多 >