在Python中通过数组索引调用函数

Dir*_*irk 20 python delegates

我在Python out1,out2,out3等中有很多函数,并且想根据我传入的整数调用它们.

def arryofPointersToFns (value):
     #call outn where n = value
Run Code Online (Sandbox Code Playgroud)

是否有捷径可寻?

nin*_*cko 38

tl; dr:写一个out(n)函数而不是out1(), out2(), ..., outN()不用这个hack.

我无法想象这个问题会在实践中出现的合理情况.请重新考虑问题的架构; 有可能有一个更好的方法来做到这一点(因为将它们存储在列表中意味着除了索引之外没有任何关于函数的意义;例如,我只能想象如果你创建的话你想要这样做一堆动态生成的thunk,它们的时序有问题,或类似的东西).特别是任何新手用户,你正在阅读这个答案,考虑制作一个更通用的功能,可以处理所有事情,或将每个功能与一些更多的识别信息相关联,或将其作为一个类的一部分,等等.

那就是说,你就是这样做的.

myFuncs = [f0,f1,f2]
myFuncs[2](...) #calls f2
Run Code Online (Sandbox Code Playgroud)

要么

myFuncs = {'alice':f1, 'bob':f2}
myFuncs['alice'](...) #calls f1
Run Code Online (Sandbox Code Playgroud)

这只是一步中的以下两个步骤:

myFuncs = [f0,f1,f2]
f = myFuncs[i]
f(...) #calls fi
Run Code Online (Sandbox Code Playgroud)

或者如果你没有像上面提到的OP那样的函数'myFunc'的注册表,你可以使用globals(),虽然它是非常hack形式并且要避免(除非你希望这些函数在你的模块命名空间中可用) ,在这种情况下可能它很好......但这可能很少这样,你可能宁愿在子模块中定义那些函数然后from mysubmodule import *它们,这反过来略微不赞成):

def fN(n):
    return globals()['f'+str(n)]

def f2():
    print("2 was called!")

fN(2)(...) #calls f2
Run Code Online (Sandbox Code Playgroud)

这里有两个其他的想法(在接受回答和前两个评论后添加):

你也可以像这样创建一个装饰器:

>>> def makeRegistrar():
...     registry = {}
...     def registrar(func):
...         registry[func.__name__] = func
...         return func  # normally a decorator returns a wrapped function, 
...                      # but here we return func unmodified, after registering it
...     registrar.all = registry
...     return registrar
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

>>> reg = makeRegistrar()
>>> @reg
... def f1(a):
...  return a+1
... 
>>> @reg
... def f2(a,b):
...  return a+b
... 
>>> reg.all
{'f1': <function f1 at 0x7fc24c381958>, 'f2': <function f2 at 0x7fc24c3819e0>}
Run Code Online (Sandbox Code Playgroud)

然后你可以调用reg.all ['f1'].您可以调整reg装饰器以跟踪索引并执行以下操作:

registry = []
index = int(re.regextofindthenumber(func.__name__))
if not index==len(registry):
    raise Exception('Expected def f{} but got def f{}')
else:
    registry[index] = func
Run Code Online (Sandbox Code Playgroud)

或者,为了避免globals(),您可以定义一个类:

class Funcs(object):
    def f1():
        ...
    def f2():
        ...
    def num(n):
        [code goes here]
Run Code Online (Sandbox Code Playgroud)

如果你的功能数量很少,你就可以逃脱['f1','f2','f3'][i].

当然,如果没有进一步的信息,所有这些建议都只是忽略了真正的问题:这种情况永远不会出现,并且可能是严重的架构缺陷的标志,当你可能宁愿拥有某些东西(使用你的例子)时:

# a possibly-better world
def out(n):
    # output to N, whatever that means
Run Code Online (Sandbox Code Playgroud)

而不是

# what you have now
def out1():
    # output to 1
def out2():
    # output to 2
def outN(n):
    # ???
Run Code Online (Sandbox Code Playgroud)

  • 虽然后者只有在一些可怜的家伙没有意识到真正的解决方案(阅读:前两个和类似的解决方案)并且让你陷入全局函数`f0`,`f1`等等时才可以接受.不要那样傻子. (5认同)

小智 3

实际上,我确实遇到了这个问题,而且非常现实:我需要显示一个表格,其中每一行都需要一种完全不同的方法来组成单元格内容。我的解决方案是创建一个返回空值的类,然后对其进行子类化并实现不同的值方法,然后将每个子类实例化为一个数组,然后根据行号调用实例的方法。通过将子类置于表生成器类的内部来限制全局命名空间污染。代码看起来像这样:

class Table(object):
    class Row(object):
        def name(self):
            return self.__class__.__name__
    class Row1(Row):
        def value(self):
            return 'A'
    class Row2(Row):
        def value(self):
            return 'B'
    class Row3(Row):
        def value(self):
            return 'C'
    def __init__(self):
        self._rows = []
        for row in self.Row.__subclasses__():
            self._row.append(row())
    def number_of_rows(self):
        return len(self._rows)
    def value(self,rownumber):
        return self._rows[rownumber].value()
Run Code Online (Sandbox Code Playgroud)

显然,在实际示例中,每个子类值方法都会有很大不同。包含“name”方法以指示如何在需要时使用内部类的任意名称提供行标题。这种方法还具有可以轻松实现合适的“大小”方法的优点。这些行将按照它们在代码中出现的顺序出现在输出中,但这可能是一个优点。

注意:上面不是经过测试的代码,只是我的实际代码的概要,用于说明一种方法。