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

33 投票
5 回答
79607 浏览
提问于 2025-04-16 15:59

我在Python里有一堆函数,比如out1、out2、out3等等。我想根据我传入的一个整数来调用这些函数。

def arryofPointersToFns (value):
     #call outn where n = value

有没有简单的方法可以做到这一点呢?

5 个回答

4

我们可以通过字典来访问方法:

def one1():
    print("Method one1 called")
def one2():
    print("Method one2 called")
def one3():
    print("Method one3 called")
methodDictionary = {1: one1, 2:one2, 3: one3}
method1 = methodDictionary[1]
method1()
method2 = methodDictionary[2]
method2()
method3 = methodDictionary[3]
method3()
55

简而言之:写一个 out(n) 函数,而不是 out1(), out2(), ..., outN(),也别费心搞这些小把戏。

我实在想不出在实际中会出现这种问题的合理场景。请重新考虑一下问题的架构;可能有更好的解决方法(因为把它们放在一个列表里意味着这些函数除了索引之外没有什么特别的意义;比如,我只能想象如果你在创建一堆动态生成的函数,并且它们的顺序很重要,才会想这样做,或者类似的情况)。特别是对于那些正在阅读这个答案的新手用户,考虑写一个更通用的函数来处理所有情况,或者给每个函数加上一些更能识别的信息,或者把它们放在一个类里面等等。

话虽如此,这就是你该怎么做。

myFuncs = [f0,f1,f2]
myFuncs[2](...) #calls f2

或者

myFuncs = {'alice':f1, 'bob':f2}
myFuncs['alice'](...) #calls f1

这其实就是把以下两个步骤合并成一步:

myFuncs = [f0,f1,f2]
f = myFuncs[i]
f(...) #calls fi

或者如果你没有像上面提到的那样的函数注册表 'myFunc',你可以使用 globals(),不过这是一种极其不推荐的做法(除非你希望这些函数在你的模块命名空间中可用,那样也许可以... 但这种情况可能很少见,你可能更愿意在一个子模块中定义这些函数,然后用 from mysubmodule import * 导入它们,这种做法也稍微有点不被推荐):

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

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

fN(2)(...) #calls f2

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

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

>>> 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

并像这样使用它:

>>> 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>}

然后你可以调用 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

另外,为了避免使用 globals(),你可以定义一个类:

class Funcs(object):
    def f1():
        ...
    def f2():
        ...
    def num(n):
        [code goes here]

如果你的函数数量不多,你可以用 ['f1','f2','f3'][i] 来解决。

当然,如果没有更多的信息,所有这些建议都只是忽视了真正的问题:这种情况根本不应该出现,这可能是一个严重架构缺陷的迹象,你可能更希望有一些(用你的例子来说)像:

# a possibly-better world
def out(n):
    # output to N, whatever that means

而不是

# what you have now
def out1():
    # output to 1
def out2():
    # output to 2
def outN(n):
    # ???
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()

显然,在实际的例子中,每个子类的值生成方法会有很大的不同。这里的'name'方法是为了说明如何提供行标题,如果需要的话,可以使用内部类的任意名称。这种方法还有一个好处,就是可以很方便地实现一个合适的'size'方法。输出的行会按照代码中的顺序出现,这可能也是一种优势。

注意:上面的代码并没有经过测试,只是我实际代码的简要概述,用来说明一种思路。

撰写回答