Cómo obtener una referencia a los atributos del módulo actual en Python

119

Lo que estoy tratando de hacer se vería así en la línea de comando:

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

¿Cómo puedo obtener una referencia a todos los nombres definidos mymoduledesde dentro?mymodule ?

Algo como esto:

# mymodule.py
names = dir(__thismodule__)
Guillermooo
fuente
Consulte también stackoverflow.com/questions/3281300/…
ksridhar

Respuestas:

135

Solo usa globals ()

globals (): devuelve un diccionario que representa la tabla de símbolos global actual. Este es siempre el diccionario del módulo actual (dentro de una función o método, este es el módulo donde se define, no el módulo desde el que se llama).

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

Maciej Pasternacki
fuente
4
¿Hay alguna forma de acceder a los gloabals () del módulo de llamada, en lugar del módulo de definición?
dimo414
9
Puede intentar obtener los datos globales de la persona que llama desde el módulo de rastreo ( docs.python.org/library/traceback.html ), pero esto está entrando en territorio de magia oscura. No sé qué estás tratando de hacer, pero es posible que desees reconsiderar tu diseño si lo necesitas.
Maciej Pasternacki
Un caso clásico de "Necesito X (para hacer Y) -> No necesitas X, necesitas Z". ¡Aunque necesito X! No se ofenda, me parece divertido, y la respuesta más votada me da la respuesta que necesito :)
pawamoy
Es importante tener en cuenta que globals () puede devolver un resultado incorrecto ya que depende del contexto en el que se llame. Por ejemplo, si realiza una llamada desde una función de clase, devolverá el contexto global vinculado a la clase, no el contexto del módulo actual, que es algo significativamente diferente. Incluso si realiza una llamada desde una función gratuita, puede devolver un contexto global de módulo diferente, dependiendo de cómo se haya importado la función.
Andry
163

Como se mencionó anteriormente, globals le brinda un diccionario en lugar de dir () que le brinda una lista de los nombres definidos en el módulo. La forma en que normalmente veo que esto se hace es así:

import sys
dir(sys.modules[__name__])
James
fuente
2
Iba a agregar un comentario de que esto no funcionaría para el módulo ' principal ' (que es como se llama el módulo que se ejecuta en la terminal) porque no parece estar en la lista de sys.modules, pero de hecho funciona :)
markm
Sin embargo, no parece funcionar desde ipdb (inserte "import ipdb; ipdb.set_trace ()" en su archivo).
gatoatigrado
9
¡Excelente! Esto solo me permitió usar la cadena de documentos del módulo actual como un mensaje de uso - sys.modules[__name__].__doc__.
george
Y para ser súper hacky. operators.attrgetter('module.attribute')(sys.modules[__name__])- ya sabes, si haces cosas locas, la gente te dice que no las hagas, como importar dinámicamente paquetes desde cadenas y luego parchearlos sin estar dentro de una clase ni nada ...
casey
2
Para cualquiera que lea el comentario de george: sys.modules[__name__].__doc__== __doc__ya que esto está definido en el espacio de nombres actual. Por lo tanto, no es necesario obtener el objeto del módulo para acceder a sus propios atributos.
Oliver Bestwalter
1

Puede que sea tarde para responder, pero no encontré la respuesta correcta para mí. La solución más cercana y precisa (más rápida que inspect.stack()) en 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

Pros :

  • Más preciso que el globals()método.
  • No depende de los marcos intermedios de la pila, que se pueden agregar, por ejemplo, mediante hooking o mediante las herramientas de 3dparty como 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
  • Puede manejar una sesión interactiva o canalizada en Python.

Contras:

  • Una especie de módulos muy precisos y que pueden devolver registrados en un ejecutable como el pytest.exeque podría no ser lo que quieres.
  • inspect.getmodule aún puede devolver None en módulos válidos dependiendo del enlace

Tengo una extensión para Python: ¿Cómo importar un módulo dada la ruta completa?

La extensión que tiene funciones de envoltura para ese 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>'):
  ...

Solo tienes que inicializar la extensión correctamente:

# 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
fuente