Jak uzyskać odniesienie do atrybutów bieżącego modułu w Pythonie

119

To, co próbuję zrobić, wyglądałoby tak w linii poleceń:

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

Jak mogę uzyskać odniesienie do wszystkich zdefiniowanych nazw mymodulez mymodulesamego siebie?

Coś takiego:

# mymodule.py
names = dir(__thismodule__)
guillermooo
źródło
sprawdź także stackoverflow.com/questions/3281300/ ...
ksridhar

Odpowiedzi:

135

Po prostu użyj globals ()

globals () - zwraca słownik reprezentujący bieżącą globalną tablicę symboli. Jest to zawsze słownik bieżącego modułu (wewnątrz funkcji lub metody jest to moduł, w którym jest on zdefiniowany, a nie moduł, z którego jest wywoływany).

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

Maciej Pasternacki
źródło
4
Czy istnieje sposób na dostęp do gloabals () modułu wywołującego zamiast do modułu definiującego?
dimo414
9
Możesz spróbować pobrać dane globalne dzwoniącego z modułu traceback ( docs.python.org/library/traceback.html ), ale to wkracza na terytorium mrocznej magii. Nie wiem, co próbujesz zrobić, ale możesz przemyśleć swój projekt, jeśli tego potrzebujesz.
Maciej Pasternacki
Klasyczny przypadek „Potrzebuję X (aby wykonać Y) -> Nie potrzebujesz X, potrzebujesz Z”. Jednak potrzebuję X! Bez obrazy, po prostu uważam to za zabawne, a najczęściej głosowana odpowiedź daje mi odpowiedź, której potrzebuję :)
pawamoy
Należy zauważyć, że metoda globals () może zwrócić nieprawidłowy wynik, ponieważ zależy to od kontekstu, w którym jest wywoływana. Na przykład, jeśli wykonasz wywołanie z funkcji klasy, zwróci globalny kontekst powiązany z klasą, a nie bieżący kontekst modułu, co jest zasadniczo inną rzeczą. Nawet jeśli wywołasz funkcję bezpłatną, może zwrócić inny kontekst globalny modułu, w zależności od tego, jak funkcja została zaimportowana.
Andry
163

Jak wspomniano wcześniej, globals udostępnia słownik, w przeciwieństwie do dir (), który zawiera listę nazw zdefiniowanych w module. Sposób, w jaki zazwyczaj to robię, wygląda następująco:

import sys
dir(sys.modules[__name__])
jamesls
źródło
2
Zamierzałem dodać komentarz, że to nie zadziała dla modułu `` main '' (tak nazywa się moduł uruchamiany na terminalu), ponieważ nie wydaje się być wymieniony w sys.modules - ale rzeczywiście działa :)
markm
Jednak wygląda na to, że nie działa z ipdb (wstaw "import ipdb; ipdb.set_trace ()" do swojego pliku).
gatoatigrado
9
Doskonały! To po prostu pozwoliło mi użyć dokumentacji bieżącego modułu jako komunikatu użycia - sys.modules[__name__].__doc__.
george
I stać się super hacky. operators.attrgetter('module.attribute')(sys.modules[__name__])- wiesz, jeśli robisz szalone rzeczy, ludzie mówią ci, żebyś nie robił, jak dynamiczne importowanie pakietów z łańcuchów, a następnie małpa łatanie tych, nie będąc w klasie, czy cokolwiek ...
casey
2
Dla każdego, kto czyta komentarz autorstwa george: sys.modules[__name__].__doc__==, __doc__ponieważ jest to zdefiniowane w bieżącej przestrzeni nazw. Pobieranie obiektu modułu w celu uzyskania dostępu do jego własnych atrybutów nie jest zatem konieczne.
Oliver Bestwalter
1

Może być późno na odpowiedź, ale sam nie znalazłem właściwej odpowiedzi. Najbliższe i najbardziej precyzyjne rozwiązanie (szybsze niż inspect.stack()) w Pythonie 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

Plusy :

  • Precyzyjniejsza niż globals()metoda.
  • Nie zależy od ramek pośrednich stosu, które można dodać, na przykład, poprzez podpięcie lub narzędzia 3dparty, takie jak 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
  • Może obsługiwać sesje potokowe lub interaktywne w języku Python.

Cons:

  • Rodzaj bardzo precyzyjny i może zwracać moduły zarejestrowane w pliku wykonywalnym, na przykład dla tego, pytest.exektóry może nie być tym, czego chcesz.
  • inspect.getmodule nadal może zwracać None na prawidłowych modułach w zależności od podpięcia

Mam rozszerzenie do pythona: Jak zaimportować moduł mając pełną ścieżkę?

Rozszerzenie posiadające funkcje opakowujące dla tego przypadku:

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

Musisz tylko poprawnie zainicjować rozszerzenie:

# 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
źródło