web-dev-qa-db-fra.com

Appelant les fonctions par index de tableau dans Python

J'ai un tas de fonctions dans Python OUT1, OUT2, OUT3, etc. et souhaitez les appeler sur la base d'un entier que je passe.

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

Y a-t-il un moyen facile de faire ceci?

21
Dirk

tL; DR: Ecrire une out(n) Fonction plutôt que out1(), out2(), ..., outN() et ne vous embêtez pas avec ce piratage.

Je ne peux pas imaginer un scénario raisonnable là où cette question se présenterait dans la pratique. Veuillez reconsidérer l'architecture du problème; Il y a probablement un moyen bien meilleur de le faire (car les stocker dans une liste implique qu'il n'y a rien de significatif sur les fonctions, à l'exception de l'indice; par exemple, je ne peux qu'imaginer que vous voudriez faire cela si vous créiez. un tas de tirons générés dynamiquement dans lesquels leur ordre temporel, ou quelque chose de similaire). Surtout les utilisateurs de novices que vous lisez cette réponse, envisagez de faire une fonction plus générale pouvant tout gérer ou de s'associer à chaque fonction d'identifier plus d'informations, ou de le coller dans le cadre d'une classe, etc.

Cela dit, c'est ainsi que vous le feriez.

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

ou

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

ceci est juste les deux étapes suivantes en une étape:

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

ou si vous n'avez pas de registre de fonctions 'myfunc' comme l'OP dit ci-dessus, vous pouvez utiliser des globaux (), bien qu'il s'agisse d'une forme extrêmement piratée et d'être évitée (sauf si vous souhaitez que ces fonctions soient disponibles dans votre espace de noms de module , dans ce cas, c'est peut-être bien ... mais c'est probablement rarement le cas, et vous préféreriez probablement définir ces fonctions dans un sous-module from mysubmodule import * eux, qui est à son tour légèrement fronçonné):

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

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

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

voici deux autres idées (ajoutées après la réponse a été acceptée et deux premiers commentaires):

Vous pouvez également créer un décorateur comme celui-ci:

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

et utilisez-le comme si:

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

ensuite, vous pouvez appeler Reg.all ['F1']. Vous pouvez adapter le décorateur reg pour garder une trace de l'indexation et faire quelque chose comme:

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

Alternativement, pour éviter globals(), vous pouvez définir une classe:

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

Si votre nombre de fonctions est petit, vous pouvez vous éloigner de ['f1','f2','f3'][i].

Bien sûr, sans autre information, toutes ces suggestions ignorent simplement le problème réel: cette situation ne devrait jamais venir, et est peut-être un signe d'une grave défaut d'architecture, lorsque vous préférez probablement avoir quelque chose (pour utiliser votre exemple) comme :

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

plutôt que

# what you have now
def out1():
    # output to 1
def out2():
    # output to 2
def outN(n):
    # ???
39
ninjagecko

En fait, j'ai exactement ce problème et c'est assez réaliste: je devais afficher une table où chaque ligne nécessite une méthode assez différente pour composer la teneur en cellule. Ma solution consistait à créer une classe qui renvoie une valeur vide, puis la sous-classement et met en œuvre des méthodes de valeur différentes, puis instanciez chaque sous-classe dans un tableau, puis appelez la méthode de l'instance en fonction du numéro de ligne. La polution globale des espaces de noms est limitée en rendant les sous-classes internes à la classe génératrice de table. Le code ressemble à quelque chose comme ça:

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

Évidemment, dans un exemple réaliste, chacune des méthodes de valeur de sous-classe serait assez différente. La méthode "nom" est incluse pour indiquer comment fournir un titre de ligne, si nécessaire, en utilisant le nom arbitraire de la classe interne. Cette approche présente également l'avantage que l'on puisse facilement mettre en œuvre une méthode de "taille" appropriée. Les lignes apparaîtront dans la sortie dans le même ordre qu'ils apparaissent dans le code, mais cela peut être un avantage.

ATTENTION: Ce qui précède n'est pas un code testé, juste une précision de mon code actuel aménagé pour illustrer une approche.

2
hamiljf
  def array(ar=[]):
     ar = np.array(ar)
     return ar
0
Josh Anish