Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Appel d'une méthode d'un module depuis son nom

26 réponses
Avatar
Alain BARTHE
Bonjour,

J'ai créé un module contenant une grand nombre de fonctions, pour
traiter chacune un type de données.

Je lance ce module avec en argument un type de données (correspondant au
nom d'une des dites fonctions).

Pour appeler la fonction en question je procède comme suit :

def create_data (key):

"""fonction d'aiguillage vers les fonctions de traitement"""

fonction = globals () [key]

for x in .... :
fonction (x)

Je vais donc chercher la référence à ma fonction dans le dictionnaire
globals().

Par exemple :
create_data ("type_1")

doit appeler la fonction

def type_1 (argument):
...
Cela fonctionne parfaitement, mais j'aurais aimé savoir si c'était le
moyen de procéder le plus "propre" ?
N'y a t-il pas une autre façon sans le dictionnaire globals() ?

Merci d'avance.

6 réponses

1 2 3
Avatar
Alain BARTHE
handraiser a écrit :
On 23 juil, 10:01, Pierre Quentel wrote:
Le seul avantage de
gérer explicitement un dictionnaire des fonctions serait d'éviter un
possible conflit de noms avec globals(), mais c'est un peu tiré par
les cheveux comme argument



On n'a pas besoin de gérer explicitement un dictionnaire. À
l'importation d'un module, Python crée lui-même dynamiquement un
dictionnaire comprenant les noms définis dans le module.

import blah
blah.__dict__

Une solution simple et 'clean' est donc de mettre toutes les fonctions
dans un module et de l'importer dans le programme qui les utilise.



Mon seul problème est qu'actuellement, j'ai tout regroupé dans un seul
script/module. Je ne peux donc pas faire d'import, sauf à réorganiser le
code, ce qui n'en vaut pas la peine dans le cas présent.

Sinon, j'ai fait des essais avec import (sur un autre module):

import mon_module
f = mon_module.__getattribute__ ("ma_fonction")
f (arguments)

Ca fonctionne parfaitement et semble équivalent à :

f = mon_module.__dict__ ["ma_fonction"]

Dans mon cas avec un seul module, il me semble que globals() revient à
retourner le dictionnaire des objets définis dans le module courant
(équivalent à __dict__ pour un module)

Y aurait-il un autre moyen pour accéder au dictionnaire du module de
plus haut niveau sans passer par globals() ?

__dict__ seul ne semble pas exister pour le module de plus hait niveau.

Dans le cas contraire, la solution : f = globals() ["ma_fonction"]
me convient très bien.


Merci pour votre aide.
Avatar
Bruno Desthuilliers
Michel Claveau - NoSpam SVP ; merci a écrit :
Bonsoir !
Certes, SWITCH n'existe pas.
Mais, on peut essayer de le simuler (voir ci-dessous)



(snip - au fait, Michel, évite de mettre le code _après_ ta signature...)

Encore une façon mimiesquement compliquée de faire un truc simple...
L'idiome le plus courant est d'utiliser un dict (ou une classe) pour
faire le dispatch...
Avatar
Bruno Desthuilliers
Alain BARTHE a écrit :
handraiser a écrit :
On 23 juil, 10:01, Pierre Quentel wrote:
Le seul avantage de
gérer explicitement un dictionnaire des fonctions serait d'éviter un
possible conflit de noms avec globals(), mais c'est un peu tiré par
les cheveux comme argument



On n'a pas besoin de gérer explicitement un dictionnaire. À
l'importation d'un module, Python crée lui-même dynamiquement un
dictionnaire comprenant les noms définis dans le module.

import blah
blah.__dict__

Une solution simple et 'clean' est donc de mettre toutes les fonctions
dans un module et de l'importer dans le programme qui les utilise.



Mon seul problème est qu'actuellement, j'ai tout regroupé dans un seul
script/module. Je ne peux donc pas faire d'import, sauf à réorganiser le
code, ce qui n'en vaut pas la peine dans le cas présent.

Sinon, j'ai fait des essais avec import (sur un autre module):

import mon_module
f = mon_module.__getattribute__ ("ma_fonction")



D'une manière générale, les noms __magiques__ sont destinés à fournir
l'implémentation pour un opérateur (ou une fonctionnalité transversale
assimilable à un opérateur), pas à être accédés directement. L'idiome
pythonesque serait:

f = getattr(mon_module, "ma_fonction")

f (arguments)

Ca fonctionne parfaitement et semble équivalent à :

f = mon_module.__dict__ ["ma_fonction"]



__getattribute__ est l'implémentation de l'opérateur de résolution
d'attributs. Par défaut (implémentation de __getattribute__ dans la
classe 'object'), cette résolution implique:

1/ rechercher dans la classe (et ses parents , suivant le mro) un
attribut de nom 'ma_fonction' qui soit un descripteur. Si oui, retourner
le résultat de l'appel à la methode __get__ du descripteur

2/ sinon, rechercher l'attribut sur l'instance (slots ou, plus
généralement, __dict__)

3/ sinon, rechercher l'attribut sur la classe et ses parent (mro encore)

4/ sinon, rechercher sur la classe (+ mro etc) une méthode
'__getattr__'. Si trouvée, retourner le résultat de l'appel à cette méthode

5/ si on arrive là, générer un AttributeError.


Comme tu peux le voir, c'est un poil plus complexe qu'un accès direct à
un_objet.__dict__, et pas tout à fait équivalent.

Dans mon cas avec un seul module, il me semble que globals() revient à
retourner le dictionnaire des objets



/objets/noms/


définis



s/définis/liés/

dans le module courant
(équivalent à __dict__ pour un module)



Il y a un moyen simple de le savoir : fait un test d'égalité entre les
deux !-)

Y aurait-il un autre moyen pour accéder au dictionnaire du module de
plus haut niveau



Qu'appelle-tu "le module de plus haut niveau" ?

sans passer par globals() ?

__dict__ seul ne semble pas exister pour le module de plus hait niveau.

Dans le cas contraire, la solution : f = globals() ["ma_fonction"]
me convient très bien.


Merci pour votre aide.


Avatar
Bruno Desthuilliers
Gael Lickindorf a écrit :
On 27 juil, 16:10, Gael Lickindorf
wrote:
pour appeler simplement une fonction à partir de son nom, utilisez
eval:

def DISQUE():
print 'fonction disque'

if __name__ == '__main__':
ligne = "DISQUE 3 5 2"
function_name = ligne.split()[0]
print function_name
eval(function_name)()

on obtient:
DISQUE
fonction disque



si la fonction est dans mon_module.py

import mon_module
module_name = 'mon_module'
function_name = 'DISQUE'
eval(module_name + '.' + function_name)()



Sans vouloir être désagréable, ici, ça passe pas une revue de code (et
ça vaut à son auteur le droit de payer les chocolatines pour toute
l'équipe).

La solution propre est évidemment d'utiliser getattr.
Avatar
Alain BARTHE
Bruno Desthuilliers a écrit :

import mon_module
f = mon_module.__getattribute__ ("ma_fonction")



D'une manière générale, les noms __magiques__ sont destinés à fournir
l'implémentation pour un opérateur (ou une fonctionnalité transversale
assimilable à un opérateur), pas à être accédés directement. L'idiome
pythonesque serait:

f = getattr(mon_module, "ma_fonction")



OK, ca semble plus propre.

f (arguments)

Ca fonctionne parfaitement et semble équivalent à :

f = mon_module.__dict__ ["ma_fonction"]



__getattribute__ est l'implémentation de l'opérateur de résolution
d'attributs. Par défaut (implémentation de __getattribute__ dans la
classe 'object'), cette résolution implique:

1/ rechercher dans la classe (et ses parents , suivant le mro) un
attribut de nom 'ma_fonction' qui soit un descripteur. Si oui, retourner
le résultat de l'appel à la methode __get__ du descripteur

2/ sinon, rechercher l'attribut sur l'instance (slots ou, plus
généralement, __dict__)

3/ sinon, rechercher l'attribut sur la classe et ses parent (mro encore)

4/ sinon, rechercher sur la classe (+ mro etc) une méthode
'__getattr__'. Si trouvée, retourner le résultat de l'appel à cette méthode

5/ si on arrive là, générer un AttributeError.


Comme tu peux le voir, c'est un poil plus complexe qu'un accès direct à
un_objet.__dict__, et pas tout à fait équivalent.




Effectivement, ca semble plus complet !!!
Merci pour ces infos que je mettrai quelques temps à digérer mais qui me
seront surement utiles un jour.


Dans mon cas avec un seul module, il me semble que globals() revient à
retourner le dictionnaire des objets



/objets/noms/


définis



s/définis/liés/

dans le module courant (équivalent à __dict__ pour un module)



Il y a un moyen simple de le savoir : fait un test d'égalité entre les
deux !-)

Y aurait-il un autre moyen pour accéder au dictionnaire du module de
plus haut niveau



Qu'appelle-tu "le module de plus haut niveau" ?




J'ai en fait un script create_header.py, qui contient ma fonction
principale, qui analyse des arguments de ligne de commande et qui
appelle, d'après son nom, une des fonctions de traitement définies dans
ce même script.

C'est ce script que j'appelle "module de plus haut niveau".

Je cherchais un moyen pour accéder au dictionnaire des noms liés à ce
module (là j'ai fait gaffe).

Si j'avais un module séparé contenant ces fonctions, je pourrais utiliser:

import mon_module
f = getattr (mon_module, "ma_fonction")

Dans mon cas, je ne sais pas quel premier argument appliquer à getattr.

C'est pourquoi j'utilise f = globals()["ma_fonction"]

Ma question initiale était juste de savoir si cette méthode était
"propre" et la plus simple à utiliser.

Merci encore pour votre aide.


sans passer par globals() ?
__dict__ seul ne semble pas exister pour le module de plus hait
niveau.

Dans le cas contraire, la solution : f = globals() ["ma_fonction"]
me convient très bien.


Merci pour votre aide.




Avatar
Bruno Desthuilliers
Alain BARTHE a écrit :
Bruno Desthuilliers a écrit :



(snip)

Y aurait-il un autre moyen pour accéder au dictionnaire du module de
plus haut niveau



Qu'appelle-tu "le module de plus haut niveau" ?




J'ai en fait un script create_header.py, qui contient ma fonction
principale, qui analyse des arguments de ligne de commande et qui
appelle, d'après son nom, une des fonctions de traitement définies dans
ce même script.

C'est ce script que j'appelle "module de plus haut niveau".

Je cherchais un moyen pour accéder au dictionnaire des noms liés à



s/à/dans/

ce
module (là j'ai fait gaffe).



Raté quand même !-)

<mode="pédant">
Bon, désolé de faire le puriste, je suis assez pénible avec ça, mais
d'un autre côté, j'ai tendance à penser qu'on ne peux pas bien
comprendre si on n'utilise pas la bonne terminologie.

La notion de 'liaison' (in english : 'binding') est fondamentale en
Python, c'est l'association, dans un espace de nommage donné, entre un
nom et une référence à un objet. L'exemple le plus évident est la (mal
nommée) assignation, ie

>> x = 42

qui dans l'exemple ci-dessus associe (lie) dans l'espace de nommage
courant le nom 'x' et l'objet int de valeur 42.

Mais il existe d'autres formes de liaison, notamment via les commandes
import, def et class.

</mode>

Si j'avais un module séparé contenant ces fonctions, je pourrais utiliser:

import mon_module
f = getattr (mon_module, "ma_fonction")

Dans mon cas, je ne sais pas quel premier argument appliquer à getattr.



Il n'y a pas par défaut de référence au module dans l'espace de nommage
du module. Mais il y en a une dans sys.modules.

# mon_module.py
import sys

def foo():
print "foo"

m = sys.modules[__name__]
print m
print getattr(m, 'foo', '???')

Ceci étant, c'est peut-être un peu overkill !-)

C'est pourquoi j'utilise f = globals()["ma_fonction"]

Ma question initiale était juste de savoir si cette méthode était
"propre" et la plus simple à utiliser.



C'est clairement la plus simple. Quand à savoir si c'est propre, ça
dépend surtout du contexte. Pour un petit programme interne, c'est
largement suffisant. Dans le cadre d'une appli / bibliothèque /
framework un peu sérieux, une solution plus explicite (quelque chose
comme le décorateur @register proposé par Alain) sera probablement
préférable.
1 2 3