Comment obtenir une référence aux attributs du module actuel en Python

119

Ce que j'essaye de faire ressemblerait à ceci dans la ligne de commande:

>>> import mymodule
>>> names = dir(mymodule)

Comment puis - je obtenir une référence à tous les noms définis à mymodulepartir de mymodulelui - même?

Quelque chose comme ça:

# mymodule.py
names = dir(__thismodule__)
Guillermooo
la source
veuillez également vérifier stackoverflow.com/questions/3281300/…
ksridhar

Réponses:

135

Utilisez simplement les globals ()

globals () - Renvoie un dictionnaire représentant la table de symboles globale actuelle. C'est toujours le dictionnaire du module courant (à l'intérieur d'une fonction ou d'une méthode, c'est le module où il est défini, pas le module à partir duquel il est appelé).

http://docs.python.org/library/functions.html#globals

Maciej Pasternacki
la source
4
Existe-t-il un moyen d'accéder aux gloabals () du module appelant, au lieu du module de définition?
dimo414
9
Vous pouvez essayer d'obtenir les globaux de l'appelant à partir du module de traceback ( docs.python.org/library/traceback.html ), mais cela entre dans le territoire de la magie noire. Je ne sais pas ce que vous essayez de faire, mais vous voudrez peut-être repenser votre conception si vous en avez besoin.
Maciej Pasternacki
Un cas classique de "J'ai besoin de X (pour faire Y) -> Vous n'avez pas besoin de X, vous avez besoin de Z". J'ai besoin de X cependant! Aucune offense, je trouve juste cela amusant, et la réponse la plus votée me donne la réponse dont j'ai besoin :)
pawamoy
Il est important de noter que les globals () peuvent renvoyer un résultat erroné car cela dépend du contexte où il est appelé. Par exemple, si vous effectuez un appel à partir d'une fonction de classe, alors il retournera le contexte global lié à la classe, pas le contexte du module actuel, ce qui est significativement différent. Même si vous effectuez un appel à partir d'une fonction libre, il peut renvoyer un contexte global de module différent, en fonction de la façon dont la fonction a été importée.
Andry
163

Comme mentionné précédemment, globals vous donne un dictionnaire par opposition à dir () qui vous donne une liste des noms définis dans le module. La façon dont je vois généralement cela est comme ceci:

import sys
dir(sys.modules[__name__])
Jamesls
la source
2
J'allais ajouter un commentaire sur le fait que cela ne fonctionnerait pas pour le module ` ` principal '' (qui s'appelle le module exécuté sur le terminal) car cela ne semble pas être répertorié dans sys.modules - mais cela fonctionne en effet :)
marque le
Cependant, cela ne semble pas fonctionner depuis ipdb (insérez "import ipdb; ipdb.set_trace ()" dans votre fichier).
gatoatigrado
9
Excellent! Cela m'a simplement permis d'utiliser la docstring du module actuel comme message d'utilisation - sys.modules[__name__].__doc__.
george
Et pour devenir super hacky. operators.attrgetter('module.attribute')(sys.modules[__name__])- vous savez, si vous faites des choses folles, les gens vous disent de ne pas faire comme importer dynamiquement des paquets à partir de chaînes, puis les patcher tout en n'étant pas dans une classe ou quoi que ce soit ...
casey
2
Pour toute personne lisant le commentaire de george: sys.modules[__name__].__doc__== __doc__car cela est défini dans l'espace de noms actuel. Il n'est donc pas nécessaire de récupérer l'objet module pour accéder à ses propres attributs.
Oliver Bestwalter
1

Il est peut-être tard pour répondre, mais je n'ai pas trouvé la bonne réponse pour moi-même. La solution la plus proche et la plus précise (plus rapide que inspect.stack()) dans le python 3.7.x:

  # search for first module in the stack
  stack_frame = inspect.currentframe()
  while stack_frame:
    print('***', stack_frame.f_code.co_name, stack_frame.f_code.co_filename, stack_frame.f_lineno)
    if stack_frame.f_code.co_name == '<module>':
      if stack_frame.f_code.co_filename != '<stdin>':
        caller_module = inspect.getmodule(stack_frame)
      else:
        # piped or interactive import
        caller_module = sys.modules['__main__']
      if not caller_module is None:
        #... do something here ...
      break
    stack_frame = stack_frame.f_back

Avantages :

  • Plus précis que la globals()méthode.
  • Ne dépend pas des cadres intermédiaires de la pile, qui peuvent être ajoutés par exemple, via hooking ou par les outils 3dparty comme pytest:
*** foo ... ..
*** boo ... ..
*** runtest c:\python\x86\37\lib\site-packages\xonsh\pytest_plugin.py 58
*** pytest_runtest_call c:\python\x86\37\lib\site-packages\_pytest\runner.py 125
*** _multicall c:\python\x86\37\lib\site-packages\pluggy\callers.py 187
*** <lambda> c:\python\x86\37\lib\site-packages\pluggy\manager.py 86
*** _hookexec c:\python\x86\37\lib\site-packages\pluggy\manager.py 92
*** __call__ c:\python\x86\37\lib\site-packages\pluggy\hooks.py 286
*** <lambda> c:\python\x86\37\lib\site-packages\_pytest\runner.py 201
*** from_call c:\python\x86\37\lib\site-packages\_pytest\runner.py 229
*** call_runtest_hook c:\python\x86\37\lib\site-packages\_pytest\runner.py 201
*** call_and_report c:\python\x86\37\lib\site-packages\_pytest\runner.py 176
*** runtestprotocol c:\python\x86\37\lib\site-packages\_pytest\runner.py 95
*** pytest_runtest_protocol c:\python\x86\37\lib\site-packages\_pytest\runner.py 80
*** _multicall c:\python\x86\37\lib\site-packages\pluggy\callers.py 187
*** <lambda> c:\python\x86\37\lib\site-packages\pluggy\manager.py 86
*** _hookexec c:\python\x86\37\lib\site-packages\pluggy\manager.py 92
*** __call__ c:\python\x86\37\lib\site-packages\pluggy\hooks.py 286
*** pytest_runtestloop c:\python\x86\37\lib\site-packages\_pytest\main.py 258
*** _multicall c:\python\x86\37\lib\site-packages\pluggy\callers.py 187
*** <lambda> c:\python\x86\37\lib\site-packages\pluggy\manager.py 86
*** _hookexec c:\python\x86\37\lib\site-packages\pluggy\manager.py 92
*** __call__ c:\python\x86\37\lib\site-packages\pluggy\hooks.py 286
*** _main c:\python\x86\37\lib\site-packages\_pytest\main.py 237
*** wrap_session c:\python\x86\37\lib\site-packages\_pytest\main.py 193
*** pytest_cmdline_main c:\python\x86\37\lib\site-packages\_pytest\main.py 230
*** _multicall c:\python\x86\37\lib\site-packages\pluggy\callers.py 187
*** <lambda> c:\python\x86\37\lib\site-packages\pluggy\manager.py 86
*** _hookexec c:\python\x86\37\lib\site-packages\pluggy\manager.py 92
*** __call__ c:\python\x86\37\lib\site-packages\pluggy\hooks.py 286
*** main c:\python\x86\37\lib\site-packages\_pytest\config\__init__.py 90
*** <module> c:\Python\x86\37\Scripts\pytest.exe\__main__.py 7
  • Peut gérer une session Python canalisée ou interactive.

Les inconvénients:

  • Une sorte de très précis et peut renvoyer des modules enregistrés dans un exécutable comme pour le pytest.exequi pourrait ne pas ce que vous voulez.
  • inspect.getmodule peut toujours renvoyer None sur les modules valides en fonction du hooking

J'ai une extension au python: comment importer un module avec le chemin complet?

L'extension ayant des fonctions wrapper pour ce cas:

def tkl_get_stack_frame_module_by_offset(skip_stack_frames = 0, use_last_frame_on_out_of_stack = False):
  ...

def tkl_get_stack_frame_module_by_name(name = '<module>'):
  ...

Vous devez simplement initialiser correctement l'extension:

# portable import to the global space
sys.path.append(<path-to-tacklelib-module-directory>)
import tacklelib as tkl

tkl.tkl_init(tkl, global_config = {'log_import_module':os.environ.get('TACKLELIB_LOG_IMPORT_MODULE')})

# cleanup
del tkl # must be instead of `tkl = None`, otherwise the variable would be still persist
sys.path.pop()

# use `tkl_*` functions directly from here ...
Andry
la source