Question

Quelle est la meilleure façon d'appeler une fonction à partir d'une chaîne portant le nom de la fonction dans un programme Python.Par exemple, disons que j'ai un module foo, et j'ai une chaîne dont le contenu est "bar".Quelle est la meilleure façon d'appeler foo.bar()?

J'ai besoin d'obtenir la valeur de retour de la fonction, c'est pourquoi je n'utilise pas simplement eval.J'ai compris comment le faire en utilisant eval pour définir une fonction temporaire qui renvoie le résultat de cet appel de fonction, mais j'espère qu'il existe une manière plus élégante de procéder.

Était-ce utile?

La solution

En supposant que le module foo avec méthode bar:

import foo
method_to_call = getattr(foo, 'bar')
result = method_to_call()

Dans la mesure où cela va, les lignes 2 et 3 peuvent être compressées en :

result = getattr(foo, 'bar')()

si cela a plus de sens pour votre cas d'utilisation.Vous pouvez utiliser getattr de cette façon sur les méthodes liées aux instances de classe, les méthodes au niveau du module, les méthodes de classe...la liste continue.

Autres conseils

locals()["myfunction"]()

ou

globals()["myfunction"]()

des locaux renvoie un dictionnaire avec une table de symboles locaux actuelle. globales renvoie un dictionnaire avec une table de symboles globale.

La solution de Patrick est probablement la plus propre.Si vous devez également récupérer le module dynamiquement, vous pouvez l'importer comme :

module = __import__('foo')
func = getattr(module, 'bar')
func()

Juste une simple contribution.Si la classe que nous devons instancier se trouve dans le même fichier, nous pouvons utiliser quelque chose comme ceci :

# Get class from globals and create an instance
m = globals()['our_class']()

# Get the function (from the instance) that we need to call
func = getattr(m, 'function_name')

# Call it
func()

Par exemple:

class A:
    def __init__(self):
        pass

    def sampleFunc(self, arg):
        print('you called sampleFunc({})'.format(arg))

m = globals()['A']()
func = getattr(m, 'sampleFunc')
func('sample arg')

# Sample, all on one line
getattr(globals()['A'](), 'sampleFunc')('sample arg')

Et, si ce n'est pas un cours :

def sampleFunc(arg):
    print('you called sampleFunc({})'.format(arg))

globals()['sampleFunc']('sample arg')

Étant donné une chaîne, avec un chemin python complet vers une fonction, voici comment j'ai procédé pour obtenir le résultat de ladite fonction :

import importlib
function_string = 'mypackage.mymodule.myfunc'
mod_name, func_name = function_string.rsplit('.',1)
mod = importlib.import_module(mod_name)
func = getattr(mod, func_name)
result = func()

La meilleure réponse selon le FAQ sur la programmation Python serait:

functions = {'myfoo': foo.bar}

mystring = 'myfoo'
if mystring in functions:
    functions[mystring]()

Le principal avantage de cette technique est que les chaînes n’ont pas besoin de correspondre aux noms des fonctions.C'est également la principale technique utilisée pour émuler une construction de cas.

La réponse (j'espère) personne n'a jamais voulu

Évaluer le comportement

getattr(locals().get("foo") or globals().get("foo"), "bar")()

Pourquoi ne pas ajouter l'importation automatique

getattr(
    locals().get("foo") or 
    globals().get("foo") or
    __import__("foo"), 
"bar")()

Si nous avons des dictionnaires supplémentaires, nous souhaitons vérifier

getattr(next((x for x in (f("foo") for f in 
                          [locals().get, globals().get, 
                           self.__dict__.get, __import__]) 
              if x)),
"bar")()

Nous devons aller plus loin

getattr(next((x for x in (f("foo") for f in 
              ([locals().get, globals().get, self.__dict__.get] +
               [d.get for d in (list(dd.values()) for dd in 
                                [locals(),globals(),self.__dict__]
                                if isinstance(dd,dict))
                if isinstance(d,dict)] + 
               [__import__])) 
        if x)),
"bar")()

Pour ce que ça vaut, si vous deviez transmettre le nom de la fonction (ou de la classe) et le nom de l'application sous forme de chaîne, vous pouvez faire ceci :

myFnName  = "MyFn"
myAppName = "MyApp"
app = sys.modules[myAppName]
fn  = getattr(app,myFnName)

Essaye ça.Bien que cela utilise toujours eval, il ne l'utilise que pour invoquer la fonction à partir du contexte actuel.Ensuite, vous disposez de la vraie fonction à utiliser comme vous le souhaitez.

Le principal avantage pour moi est que vous obtiendrez des erreurs liées à l'évaluation au moment de l'appel de la fonction.Ensuite, vous obtiendrez seulement les erreurs liées à la fonction lorsque vous appelez.

def say_hello(name):
    print 'Hello {}!'.format(name)

# get the function by name
method_name = 'say_hello'
method = eval(method_name)

# call it like a regular function later
args = ['friend']
kwargs = {}
method(*args, **kwargs)

rien de ce qui a été suggéré ne m'a aidé.J'ai pourtant découvert cela.

<object>.__getattribute__(<string name>)(<params>)

J'utilise python 2.66

J'espère que cela t'aides

Comme cette question Comment appeler dynamiquement des méthodes au sein d'une classe en utilisant l'affectation du nom de méthode à une variable [dupliquer] marqué comme doublon comme celui-ci, je poste une réponse connexe ici :

Le scénario est le suivant : une méthode dans une classe souhaite appeler dynamiquement une autre méthode sur la même classe. J'ai ajouté quelques détails à l'exemple original qui offre un scénario plus large et plus clair :

class MyClass:
    def __init__(self, i):
        self.i = i

    def get(self):
        func = getattr(MyClass, 'function{}'.format(self.i))
        func(self, 12)   # This one will work
        # self.func(12)    # But this does NOT work.


    def function1(self, p1):
        print('function1: {}'.format(p1))
        # do other stuff

    def function2(self, p1):
        print('function2: {}'.format(p1))
        # do other stuff


if __name__ == "__main__":
    class1 = MyClass(1)
    class1.get()
    class2 = MyClass(2)
    class2.get()

Sortie (Python 3.7.x)

fonction1 :12

fonction2 :12

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top