编辑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
将获得所有顶级函数。(我在这里假设testall
或test1
正在从if __name__ == '__main__'
模块底部的块中调用,或者另一个脚本正在执行import tests; tests.test1(10)
,或类似的事情。)
事实上,即使你明确命名test1
and 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
andinspect
和test1
是全局的(因为你没有分配给它们 intestall
或 in an封闭范围)。
接下来,解释器按顺序执行顶级模块的编译字节码。这包括执行函数定义。所以,testall
变成一个函数,然后test1
变成一个函数,然后test2
变成一个函数。(函数实际上只是适当的编译代码,附加了一些额外的东西,比如定义它的全局命名空间。)
稍后,当您调用该testall
函数时,解释器会执行该函数。这是列表理解(在第一个版本中)或全局名称查找(在第二个版本中)发生的时候。由于 和 的函数定义test1
已经test2
被评估并绑定到模块中的全局名称,所以一切正常。
如果您改为稍后调用test1
,哪个调用testall
呢?没问题。解释器执行test1
,它调用了testall
,这显然已经定义了,所以解释器调用它,其余的与上一段相同。
那么,如果您在and定义之间testall
或test1
之间进行调用呢?在那种情况下,还没有定义,所以它不会出现在列表中(第一个版本),或者会引发一个(第二个版本)。但只要你不这样做,就没有问题。而且没有充分的理由这样做。test1
test2
test2
NameError
如果您担心testfunctions
每次调用时计算的可怕性能成本testall
……嗯,首先,这是一个愚蠢的担心;你要打多少次?您的功能真的如此之快,以至于调用和过滤的时间getmembers
甚至出现在雷达上吗?但如果真的很担心,只需以您最喜欢的常用方式缓存值 - 可变默认值、全局私有、函数属性……:
def testall(arg, _functions_cache=[]):
if not _functions_cache:
_functions_cache.extend([…])