Як отримати посилання на атрибути поточного модуля в Python


119

Що я намагаюся зробити, виглядатиме так у командному рядку:

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

Як я можу отримати посилання на всі імена, визначені mymoduleвсередині mymoduleсебе?

Щось на зразок цього:

# mymodule.py
names = dir(__thismodule__)

Будь ласка, перевірте stackoverflow.com/questions/3281300 / ...
— ksridhar

Відповіді:


135

Просто використовуйте глобалі ()

globals () - Повернення словника, що представляє поточну таблицю глобальних символів. Це завжди словник поточного модуля (всередині функції або методу, це модуль, де він визначений, а не модуль, з якого він викликається).

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


4
Чи є якийсь спосіб отримати доступ до gloabals () викличного модуля замість визначального модуля?
— dimo414

9
Ви можете спробувати отримати глобальний номер абонента з модуля traceback ( docs.python.org/library/traceback.html ), але це потрапляє на територію темної магії. Я не знаю, що ви намагаєтеся зробити, але ви, можливо, захочете переосмислити свій дизайн.
— Мацей Пастернацький

Класичний випадок "Мені потрібен X (щоб завершити Y) -> Вам не потрібен X, потрібен Z". Мені все-таки потрібно X! Без образи, я просто вважаю це кумедним, і найбільш голосована відповідь дає мені потрібну мені відповідь :)
— pawamoy

Важливо зазначити, що глобалі () можуть повернути неправильний результат, оскільки це залежить від контексту, де викликається. Наприклад, якщо здійснити виклик з класової функції, то він поверне глобальний контекст, пов'язаний з класом, а не поточний контекст модуля, що суттєво відрізняється. Навіть якщо здійснити виклик від вільної функції, він може повернути інший модульний глобальний контекст, залежно від способу імпорту.
— Андрій

163

Як вже було сказано раніше, глобалі надає словник на відміну від dir (), який дає вам список імен, визначених у модулі. Я зазвичай бачу це зроблено так:

import sys
dir(sys.modules[__name__])

2
Я збирався додати коментар, що це не буде працювати для " головного " модуля (так називається модуль, що працює в терміналі), оскільки це, схоже, не вказано в sys.modules - але це дійсно працює :)
— markm

Однак, схоже, це не працює з ipdb (вставте "імпорт ipdb; ipdb.set_trace ()" у ваш файл).
— gatoatigrado

9
Відмінно! Це просто дозволило мені використовувати доктрину поточного модуля як повідомлення про використання - sys.modules[__name__].__doc__.
— Джордж

І отримати супер хакі. operators.attrgetter('module.attribute')(sys.modules[__name__])- ви знаєте, якщо ви робите божевільні речі, люди говорять вам не робити динамічно імпортувати пакунки з рядків, а потім мавпа виправляти їх, не перебуваючи в класі чи що-небудь ...
— casey

2
Для всіх, хто читає коментар від george: sys.modules[__name__].__doc__== __doc__оскільки це визначено в поточному просторі імен. Тому отримання об'єкта модуля для доступу до його власних атрибутів не є необхідним.
— Oliver Bestwalter

1

Можливо, пізно відповісти, але я не знайшов правильної відповіді для себе. Найбільш близьке і точне рішення (швидше, ніж inspect.stack()) в пітоні 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

Плюси :

  • Точніше, ніж globals()метод.
  • Не залежить від проміжних кадрів стека, які можна додати, наприклад, за допомогою підключення або за допомогою інструментів 3dparty, таких як 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
  • Може обробляти пітфонні або інтерактивні сеанси.

Мінуси:

  • Різновиди, які є дуже точними і можуть повертати модулі, зареєстровані у виконуваному файлі, на зразок pytest.exeякого може бути не те, що ви хочете.
  • inspect.getmodule все ще може повернути None на дійсних модулях залежно від підключення

У мене є розширення до python: Як імпортувати модуль з урахуванням повного шляху?

Розширення, що має функції обгортки для цього випадку:

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

Ви повинні просто ініціалізувати розширення належним чином:

# 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 ...
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.