Appel d'une fonction d'un module en utilisant son nom (une chaîne)

1736

Quelle est la meilleure façon d'appeler une fonction en fonction d'une chaîne avec 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 ne l'utilise pas seulement eval. J'ai compris comment le faire en utilisant evalpour définir une fonction temporaire qui renvoie le résultat de cet appel de fonction, mais j'espère qu'il existe une façon plus élégante de le faire.

ricree
la source

Réponses:

2081

En supposant que le module fooavec la méthode bar:

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

Vous pouvez raccourcir les lignes 2 et 3 pour:

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

si cela a plus de sens pour votre cas d'utilisation.

Vous pouvez utiliser getattrde 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.

Patrick Johnmeyer
la source
9
hasattr ou getattr peuvent être utilisés pour déterminer si une fonction est définie. J'avais un mappage de base de données (eventType et gestion de functionName) et je voulais m'assurer de n'avoir jamais "oublié" de définir un gestionnaire d'événements dans mon python
Shaun
9
Cela fonctionne si vous connaissez déjà le nom du module. Cependant, si vous souhaitez que l'utilisateur fournisse le nom du module sous forme de chaîne, cela ne fonctionnera pas.
Blairg23
7
Si vous devez éviter une exception NoneType n'est pas appelable, vous pouvez également utiliser la forme à trois arguments de getattr: getattr (foo, 'bar', lambda: None). Je m'excuse pour le formatage; l'application android stackexchange est apparemment terrible.
geekofalltrades
3
Voir aussi la réponse fournie par @sastanin si vous ne vous souciez par exemple que des fonctions de votre module local / actuel.
NuSkooler
6
@akki Oui, si vous êtes dans le foomodule, vous pouvez le globals()faire:methodToCall = globals()['bar']
Ben Hoyt
544
locals()["myfunction"]()

ou

globals()["myfunction"]()

locals renvoie un dictionnaire avec une table de symboles locale actuelle. globals renvoie un dictionnaire avec une table de symboles globale.

sastanin
la source
53
Cette méthode avec globals / localals est bonne si la méthode que vous devez appeler est définie dans le même module que vous appelez.
Joelmob
@Joelmob existe-t-il un autre moyen d'extraire un objet par chaîne de l'espace de noms racine?
Nick T
@NickT Je ne connais que ces méthodes, je ne pense pas qu'il y en ait d'autres qui remplissent la même fonction que celles-ci, au moins je ne peux pas penser à une raison pour laquelle il devrait y en avoir plus.
Joelmob
J'ai une raison pour vous (en fait ce qui m'a amené ici): le module A a une fonction F qui doit appeler une fonction par son nom. Le module B importe le module A et appelle la fonction F avec une demande d'appel à la fonction G, qui est définie dans le module B. Cet appel échoue car, apparemment, la fonction F s'exécute uniquement avec les globaux définis dans le module F - donc globals () ['G'] = Aucun.
David Stein
338

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

module = __import__('foo')
func = getattr(module, 'bar')
func()
HS.
la source
93
Je ne comprends pas ce dernier commentaire. __import__ a son propre droit et la phrase suivante dans les documents mentionnés dit: "L'utilisation directe de __import __ () est rare, sauf dans les cas où vous souhaitez importer un module dont le nom n'est connu qu'au moment de l'exécution". Donc: +1 pour la réponse donnée.
hoffmaje
50
Utilisez importlib.import_module. Les documents officiels disent à propos de __import__: "Il s'agit d'une fonction avancée qui n'est pas nécessaire dans la programmation Python de tous les jours, contrairement à importlib.import_module ()." docs.python.org/2/library/functions.html#__import__
glarrain
8
@glarrain Tant que vous êtes d'accord avec seulement le support 2.7 et plus.
Xiong Chiamiov
1
@Xiong Chaimiov, importlib.import_moduleest pris en charge dans 3.6. Voir docs.python.org/3.6/library/…
cowlinator
7
@cowlinator Oui, 3.6 fait partie de "2.7 et plus", à la fois dans la sémantique de versioning stricte et dans les dates de sortie (il est arrivé environ six ans plus tard). Il n'existait pas non plus pendant trois ans après mon commentaire. ;) Dans la branche 3.x, le module existe depuis la 3.1. 2.7 et 3.1 sont maintenant assez anciens; vous trouverez toujours des serveurs qui ne prennent en charge que 2.6, mais il vaut probablement la peine d'avoir importlib comme conseil standard de nos jours.
Xiong Chiamiov
114

Juste une simple contribution. Si la classe que nous devons instance est 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, sinon une classe:

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

globals()['sampleFunc']('sample arg')
Sourcegeek
la source
101

Étant donné une chaîne, avec un chemin python complet vers une fonction, voici comment j'ai obtenu 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()
roue ferreuse
la source
1
Cela m'a aidé. C'est une version légère de la __import__fonction.
Pankaj Bhambhani
Je pense que c'était la meilleure réponse.
Dit le
55

La meilleure réponse selon la FAQ de 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 aussi la principale technique utilisée pour émuler une construction de cas


la source
43

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

Comportement similaire à Eval

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

Dans le cas où nous aurions des dictionnaires supplémentaires, nous voulons 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")()
00500005
la source
cela pourrait être amélioré en scannant récursivement l'arborescence des répertoires et les lecteurs USB à montage automatique
wilks
27

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

myFnName  = "MyFn"
myAppName = "MyApp"
app = sys.modules[myAppName]
fn  = getattr(app,myFnName)
trubliphone
la source
Un peu plus générique esthandler = getattr(sys.modules[__name__], myFnName)
lony
21

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

Le principal avantage pour moi de cela est que vous obtiendrez toutes les erreurs liées à l'évaluation au moment de l'invocation de la fonction. Ensuite, vous n'obtiendrez que 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)
tvt173
la source
1
Ce serait risqué. la chaîne peut avoir n'importe quoi et eval finirait par l'évaluer sans aucune considération.
iankit
4
Bien sûr, vous devez être conscient du contexte dans lequel vous l'utilisez, que cela soit approprié ou non, compte tenu de ces risques.
tvt173
1
Une fonction ne devrait pas être responsable de la validation de ses paramètres - c'est le travail d'une fonction différente. Dire qu'il est risqué d'utiliser eval avec une chaîne, c'est dire que l'utilisation de chaque fonction est risquée.
red777
Vous ne devez jamais utiliser evalsauf si strictement nécessaire. getattr(__module__, method_name)est un bien meilleur choix dans ce contexte.
moi
14

rien de ce qui a été suggéré ne m'a aidé. Je l'ai découvert cependant.

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

J'utilise python 2.66

J'espère que cela t'aides

Natdrip
la source
13
Sous quel aspect est-ce mieux que getattr ()?
V13
1
Exactement ce que je voulais. Fonctionne comme un charme! Parfait!! self.__getattribute__('title')est égal àself.title
ioaniatr
self.__getattribute__('title')ne fonctionne pas dans tous les cas (je ne sais pas pourquoi) après tout, mais le func = getattr(self, 'title'); func();fait. Donc, il vaut peut-être mieux utiliser à la getattr()place
ioaniatr
10
Les personnes qui ne connaissent pas le python peuvent-elles arrêter de voter pour cette ordure? Utilisez getattrplutôt.
Aran-Fey
6

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

Le scénario est, une méthode dans une classe veut appeler une autre méthode sur la même classe dynamiquement, j'ai ajouté quelques détails à l'exemple original qui offre un scénario plus large et une clarté:

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

Serjik
la source
-7

Ceci est une réponse simple, cela vous permettra d'effacer l'écran par exemple. Il y a deux exemples ci-dessous, avec eval et exec, qui afficheront 0 en haut après le nettoyage (si vous utilisez Windows, passez clearà cls, les utilisateurs Linux et Mac laissent tel quel par exemple) ou simplement l'exécutent, respectivement.

eval("os.system(\"clear\")")
exec("os.system(\"clear\")")
Fichier numérique
la source
3
Ce n'est pas ce qu'op demande.
Tuncay Göncüoğlu