Come ottenere un riferimento agli attributi del modulo corrente in Python


119

Quello che sto cercando di fare sarebbe simile a questo nella riga di comando:

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

Come posso ottenere un riferimento a tutti i nomi definiti mymoduleall'interno di mymodulese stesso?

Qualcosa come questo:

# mymodule.py
names = dir(__thismodule__)

Risposte:


135

Usa solo globals ()

globals () - Restituisce un dizionario che rappresenta la tabella dei simboli globale corrente. Questo è sempre il dizionario del modulo corrente (all'interno di una funzione o di un metodo, questo è il modulo dove è definito, non il modulo da cui è chiamato).

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


4
C'è un modo per accedere a gloabals () del modulo chiamante, invece del modulo di definizione?
dimo414

9
Puoi provare a ottenere le variabili globali del chiamante dal modulo traceback ( docs.python.org/library/traceback.html ), ma questo sta entrando nel territorio della magia oscura. Non so cosa stai cercando di fare, ma potresti voler ripensare al tuo design se ne hai bisogno.
Maciej Pasternacki

Un classico caso di "Ho bisogno di X (per fare Y) -> Non hai bisogno di X, hai bisogno di Z". Ho bisogno di X però! Senza offesa, lo trovo solo divertente, e la risposta più votata mi dà la risposta di cui ho bisogno :)
pawamoy

È importante notare che globals () può restituire un risultato errato poiché dipende dal contesto in cui viene chiamato. Ad esempio, se si effettua una chiamata da una funzione di classe, restituirà il contesto globale collegato alla classe, non il contesto del modulo corrente, che è una cosa significativamente diversa. Anche se si effettua una chiamata da una funzione libera, può restituire un contesto globale del modulo diverso, a seconda di come la funzione è stata importata.
Andry

163

Come accennato in precedenza, globals ti dà un dizionario al contrario di dir () che ti dà un elenco dei nomi definiti nel modulo. Il modo in cui in genere vedo questo fatto è come questo:

import sys
dir(sys.modules[__name__])

2
Stavo per aggiungere un commento secondo cui questo non funzionerebbe per il modulo ' principale ' (che è quello che viene chiamato il modulo eseguito al terminale) perché non sembra essere elencato in sys.modules, ma funziona davvero :)
markm

Tuttavia, non sembra funzionare da ipdb (inserisci "import ipdb; ipdb.set_trace ()" nel tuo file).
gatoatigrado

9
Eccellente! Questo mi ha solo permesso di usare la docstring del modulo corrente come messaggio di utilizzo - sys.modules[__name__].__doc__.
george

E per diventare super hacky. operators.attrgetter('module.attribute')(sys.modules[__name__])- sai, se fai cose pazze le persone ti dicono di non fare come importare dinamicamente i pacchetti dalle stringhe e poi applicare le patch di scimmia mentre non sei all'interno di una classe o altro ...
casey

2
Per chiunque legga il commento di george: sys.modules[__name__].__doc__== __doc__poiché è definito nello spazio dei nomi corrente. Il recupero dell'oggetto modulo per accedere ai propri attributi non è quindi necessario.
Oliver Bestwalter

1

Potrebbe essere tardi per rispondere, ma non ho trovato la risposta corretta per me stesso. La soluzione più vicina e precisa (più veloce di inspect.stack()) nel 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

Pro :

  • Più preciso del globals()metodo.
  • Non dipende dalla pila di frame intermedi, che possono essere aggiunti ad esempio tramite hooking o dagli strumenti 3dparty come 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
  • Può gestire sessioni interattive o piped Python.

Contro:

  • Un tipo molto preciso e può restituire moduli registrati in un eseguibile come per il pytest.exequale potrebbe non essere quello che vuoi.
  • inspect.getmodule può ancora restituire None su moduli validi a seconda dell'hooking

Ho un'estensione per il python: come importare un modulo dato il percorso completo?

L'estensione con funzioni wrapper per quel caso:

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>'):
  ...

Devi solo inizializzare correttamente l'estensione:

# 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 ...
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.