19

我有以下代码

fset = [ obj for name,obj in inspect.getmembers(sys.modules[__name__]) if inspect.isfunction(obj) ]

def func(num):
    pass

if __name__ == "__main__":
    print(fset)

印刷

[]

然而这

def func(num):
    pass

fset = [ obj for name,obj in inspect.getmembers(sys.modules[__name__]) if inspect.isfunction(obj) ]

if __name__ == "__main__":
    print(fset)

印刷

[<function func at 0x7f35c29383b0>]

那么 fset 如何成为当前模块中所有函数的列表,其中 fset 在所有函数的顶部定义?

编辑1:我想做的是

def testall(arg):
    return any(f(arg) for f in testfunctions)

def test1(arg):
    #code here
    # may call testall but wont call anyother test*

def test2(arg):
    #code here
    # may call testall but wont call anyother test*

未来可能会增加更多的测试功能。这就是 fset/testfunctions 的原因

4

3 回答 3

17

编辑1:我想做的是

def testall(arg):
    return any(f(arg) for f in testfunctions)

def test1(arg):
    #code here
    # may call testall but wont call anyother test*    

这工作得很好:

def testall(arg):
    testfunctions = [obj for name,obj in inspect.getmembers(sys.modules[__name__]) 
                     if (inspect.isfunction(obj) and 
                         name.startwith('test') and name != 'testall')]
    return any(f(arg) for f in testfunctions)

def test1(arg):
    #code here
    # may call testall but wont call anyother test*

在这种情况下,testfunctions直到testall被调用才被评估,所以这里没有问题——到那时,所有顶级模块代码(包括test1定义)都将被评估,因此testfunctions将获得所有顶级函数。(我在这里假设testalltest1正在从if __name__ == '__main__'模块底部的块中调用,或者另一个脚本正在执行import tests; tests.test1(10),或类似的事情。)

事实上,即使你明确命名test1and test2,也不会有问题:

def testall(arg):
    testfunctions = ('test1',)
    return any(f(arg) for f in testfunctions)

def test1(arg):
    #code here
    # may call testall but wont call anyother test*

同样,test1在您调用时已经定义了testall,所以一切都很好。


如果你想了解为什么会这样,你必须了解这里的阶段。

当您导入模块或运行顶级脚本时,第一阶段是编译(除非已经有一个缓存的 .pyc 文件)。编译器不需要知道名称的值是什么,只需要知道它是本地的还是全局的(或闭包单元),它已经可以分辨出sysandinspecttest1是全局的(因为你没有分配给它们 intestall或 in an封闭范围)。

接下来,解释器按顺序执行顶级模块的编译字节码。这包括执行函数定义。所以,testall变成一个函数,然后test1变成一个函数,然后test2变成一个函数。(函数实际上只是适当的编译代码,附加了一些额外的东西,比如定义它的全局命名空间。)

稍后,当您调用该testall函数时,解释器会执行该函数。这是列表理解(在第一个版本中)或全局名称查找(在第二个版本中)发生的时候。由于 和 的函数定义test1已经test2被评估并绑定到模块中的全局名称,所以一切正常。

如果您改为稍后调用test1,哪个调用testall呢?没问题。解释器执行test1,它调用了testall,这显然已经定义了,所以解释器调用它,其余的与上一段相同。

那么,如果您在and定义之间testalltest1之间进行调用呢?在那种情况下,还没有定义,所以它不会出现在列表中(第一个版本),或者会引发一个(第二个版本)。但只要你不这样做,就没有问题。而且没有充分的理由这样做。test1test2test2NameError


如果您担心testfunctions每次调用时计算的可怕性能成本testall……嗯,首先,这是一个愚蠢的担心;你要打多少次?您的功能真的如此之快,以至于调用和过滤的时间getmembers甚至出现在雷达上吗?但如果真的很担心,只需以您最喜欢的常用方式缓存值 - 可变默认值、全局私有、函数属性……:

def testall(arg, _functions_cache=[]):
    if not _functions_cache:
        _functions_cache.extend([…])
于 2013-09-20T17:42:59.153 回答
11

不可能。函数定义在 Python中执行。在执行其定义之前,这些函数不存在。fset在定义函数之前无法定义您的变量。

于 2013-09-20T01:55:05.860 回答
4

要排除任何导入的函数,这可行:

import sys
import inspect 
   [obj for name,obj in inspect.getmembers(sys.modules[__name__]) 
                         if (inspect.isfunction(obj) and 
                             name.startswith('test') and
                             obj.__module__ == __name__)]
于 2020-08-14T12:51:05.933 回答