EDIT 1: What I am trying to do isdef 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将获得所有顶级函数。(我假设testall或test1是从模块底部的if __name__ == '__main__'块调用的,或者另一个脚本正在执行import tests; tests.test1(10)或类似的操作。)
实际上,即使您显式地命名了test1和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文件)。编译器不需要知道一个名称有什么值,只需要知道它是本地的还是全局的(或者是一个闭包单元格),它已经可以知道sys和inspect和test1是全局的(因为您没有在testall或封闭的作用域中分配给它们)。
接下来,解释器按顺序执行顶级模块的编译字节码。这包括执行函数定义。因此,testall变成一个函数,然后test1变成一个函数,然后test2变成一个函数。(函数实际上只是适当的编译代码,附加了一些额外的东西,比如它在中定义的全局命名空间。)
稍后,当您调用testall函数时,解释器将执行该函数。这是当列表理解(在第一个版本中)或全局名称查找(在第二个版本中)发生时。由于test1和test2的函数定义已经被求值并绑定到模块中的全局名称,所以一切正常。
如果您稍后调用test1,它调用testall,会怎么样?没问题。解释器执行test1,它有一个对testall的调用,这个调用显然已经定义好了,所以解释器调用它,剩下的与前面的段落相同。
那么,如果在test1和test2定义之间调用testall或test1呢?在这种情况下,test2还没有定义,因此它不会出现在列表中(第一个版本),或者引发一个NameError(第二个版本)。但只要你不这么做,就没问题。也没有理由这么做。
如果你担心每次你打电话给testall时计算的可怕性能成本……那么,首先,这是一个愚蠢的担心;你要打多少次电话?你的函数真的如此之快以至于调用和过滤的时间甚至出现在雷达上了吗?但如果真的很担心,只需将值缓存到您最喜欢的可变默认值、privat global、function attribute、…:def testall(arg, _functions_cache=[]):
if not _functions_cache:
_functions_cache.extend([…])