Cara mendapatkan referensi ke atribut modul saat ini dengan Python

119

Apa yang saya coba lakukan akan terlihat seperti ini di baris perintah:

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

Bagaimana saya bisa mendapatkan referensi ke semua nama yang didefinisikan mymoduledari dalam mymoduledirinya sendiri?

Sesuatu seperti ini:

# mymodule.py
names = dir(__thismodule__)
guillermooo
sumber
harap juga periksa stackoverflow.com/questions/3281300/…
ksridhar

Jawaban:

135

Cukup gunakan global ()

globals () - Kembalikan kamus yang mewakili tabel simbol global saat ini. Ini selalu merupakan kamus dari modul saat ini (di dalam fungsi atau metode, ini adalah modul tempat ia didefinisikan, bukan modul tempat ia dipanggil).

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

Maciej Pasternacki
sumber
4
Apakah ada cara untuk mengakses gloabals () dari modul pemanggil, selain dari modul penentu?
dimo414
9
Anda dapat mencoba mendapatkan global penelepon dari modul traceback ( docs.python.org/library/traceback.html ), tetapi ini memasuki wilayah sihir gelap. Saya tidak tahu apa yang Anda coba lakukan, tetapi Anda mungkin ingin memikirkan kembali desain Anda jika membutuhkannya.
Maciej Pasternacki
Kasus klasik "Saya butuh X (untuk menyelesaikan Y) -> Anda tidak membutuhkan X, Anda memerlukan Z". Aku memang butuh X! Jangan tersinggung, saya hanya menganggap ini lucu, dan jawaban yang paling banyak dipilih memberi saya jawaban yang saya butuhkan :)
pawamoy
Penting untuk diperhatikan bahwa globals () dapat mengembalikan hasil yang salah karena bergantung pada konteks tempat dipanggil. Misalnya, jika melakukan panggilan dari fungsi kelas, maka itu akan mengembalikan konteks global yang ditautkan ke kelas, bukan konteks modul saat ini, yang secara signifikan berbeda. Meskipun melakukan panggilan dari fungsi gratis, ia dapat mengembalikan konteks global modul yang berbeda, bergantung pada bagaimana fungsi tersebut diimpor.
Andry
163

Seperti yang disebutkan sebelumnya, globals memberi Anda kamus, bukan dir () yang memberi Anda daftar nama yang ditentukan dalam modul. Cara saya biasanya melihat ini selesai adalah seperti ini:

import sys
dir(sys.modules[__name__])
jamesls
sumber
2
Saya akan menambahkan komentar bahwa ini tidak akan berfungsi untuk modul ' utama ' (yang disebut modul yang dijalankan di terminal) karena itu tampaknya tidak terdaftar di sys.modules - tetapi memang berfungsi :)
tandai
Namun, sepertinya tidak bekerja dari ipdb (masukkan "import ipdb; ipdb.set_trace ()" ke dalam file Anda).
gatoatigrado
9
Luar biasa! Ini hanya memungkinkan saya untuk menggunakan docstring modul saat ini sebagai pesan penggunaan - sys.modules[__name__].__doc__.
george
Dan untuk menjadi super hacky. operators.attrgetter('module.attribute')(sys.modules[__name__])- Anda tahu, jika Anda melakukan hal-hal gila, orang-orang menyuruh Anda untuk tidak melakukan seperti mengimpor paket secara dinamis dari string dan kemudian menambalnya saat tidak berada dalam kelas atau apa pun ...
casey
2
Untuk siapa pun yang membaca komentar oleh george: sys.modules[__name__].__doc__== __doc__karena ini didefinisikan dalam namespace saat ini. Oleh karena itu, mengambil objek modul untuk mengakses atributnya sendiri tidak diperlukan.
Oliver Bestwalter
1

Mungkin terlambat untuk menjawab, tetapi saya tidak menemukan jawaban yang benar untuk diri saya sendiri. Solusi paling dekat dan tepat (lebih cepat dari inspect.stack()) di 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

Kelebihan :

  • Lebih berharga daripada globals()metode.
  • Tidak bergantung pada bingkai perantara tumpukan, yang dapat ditambahkan misalnya, melalui pengait atau oleh alat 3dparty seperti 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
  • Dapat menangani sesi pipa python atau interaktif.

Kekurangan:

  • Semacam yang lebih tepat dan dapat mengembalikan modul yang terdaftar dalam bentuk yang dapat dieksekusi seperti pytest.exeyang mungkin bukan yang Anda inginkan.
  • inspect.getmodule masih dapat mengembalikan None pada modul yang valid tergantung pada hooking

Saya memiliki ekstensi ke python: Bagaimana cara mengimpor modul yang diberikan jalur lengkap?

Ekstensi yang memiliki fungsi pembungkus untuk kasus tersebut:

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

Anda harus menginisialisasi ekstensi dengan benar:

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