Como obter uma referência para os atributos do módulo atual em Python


119

O que estou tentando fazer ficaria assim na linha de comando:

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

Como posso obter uma referência para todos os nomes definidos no mymodulede dentro mymodulesi?

Algo assim:

# mymodule.py
names = dir(__thismodule__)

Respostas:


135

Basta usar globais ()

globals () - Retorna um dicionário que representa a tabela de símbolos global atual. Este é sempre o dicionário do módulo atual (dentro de uma função ou método, este é o módulo onde ele é definido, não o módulo a partir do qual é chamado).

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


4
Existe alguma maneira de acessar o gloabals () do módulo de chamada, em vez do módulo de definição?
dimo414

9
Você pode tentar obter os globais do chamador do módulo traceback ( docs.python.org/library/traceback.html ), mas isso está entrando no território da magia negra. Não sei o que você está tentando fazer, mas você pode querer repensar seu design se precisar.
Maciej Pasternacki

Um caso clássico de "Eu preciso de X (para fazer Y) -> Você não precisa de X, você precisa de Z". Eu preciso de X embora! Sem ofensa, acho isso engraçado, e a resposta mais votada me dá a resposta de que preciso :)
pawamoy

É importante observar que o globals () pode retornar um resultado errado, pois depende do contexto em que está sendo chamado. Por exemplo, se fizer uma chamada de uma função de classe, ele retornará o contexto global vinculado à classe, não o contexto do módulo atual, que é uma coisa significativamente diferente. Mesmo se fizer uma chamada de uma função livre, ela pode retornar um contexto global de módulo diferente, dependendo de como a função foi importada.
Andry

163

Conforme mencionado anteriormente, os globais fornecem um dicionário em vez de dir (), que fornece uma lista dos nomes definidos no módulo. A maneira como normalmente vejo isso é assim:

import sys
dir(sys.modules[__name__])

2
Eu ia adicionar um comentário que isso não funcionaria para o módulo ' principal ' (que é como o módulo executado no terminal é chamado) porque ele não parece estar listado em sys.modules - mas realmente funciona :)
markm

No entanto, parece não funcionar a partir do ipdb (insira "import ipdb; ipdb.set_trace ()" em seu arquivo).
gatoatigrado de

9
Excelente! Isso apenas me permitiu usar a docstring do módulo atual como uma mensagem de uso - sys.modules[__name__].__doc__.
george

E para ficar super hacky. operators.attrgetter('module.attribute')(sys.modules[__name__])- você sabe, se você fizer coisas malucas que as pessoas lhe dizem para não fazer, como importar pacotes dinamicamente de strings e então fazer monkey patch neles enquanto não estiver em uma classe ou qualquer coisa ...
casey

2
Para qualquer um que esteja lendo o comentário de george: sys.modules[__name__].__doc__== __doc__como definido no namespace atual. Buscar o objeto do módulo para acessar seus próprios atributos, portanto, não é necessário.
Oliver Bestwalter

1

Pode ser tarde para responder, mas não encontrei a resposta correta para mim. A solução mais próxima e precisa (mais rápida do que inspect.stack()) no 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

Prós :

  • Mais preciso do que o globals()método.
  • Não depende dos frames intermediários da pilha, que podem ser adicionados, por exemplo, via hooking ou pelas ferramentas 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
  • Pode lidar com python canalizado ou sessão interativa.

Contras:

  • Um tipo bem preciso e pode retornar módulos cadastrados em um executável como para o pytest.exequal pode não o que você deseja.
  • inspect.getmodule ainda pode retornar None em módulos válidos, dependendo do gancho

Eu tenho uma extensão para o python: como importar um módulo dado o caminho completo?

A extensão com funções de wrapper para esse 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>'):
  ...

Você precisa apenas inicializar a extensão corretamente:

# 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 ...
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.