วิธีรับการอ้างอิงถึงแอตทริบิวต์ของโมดูลปัจจุบันใน Python


119

สิ่งที่ฉันพยายามจะมีลักษณะเช่นนี้ในบรรทัดคำสั่ง:

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

ฉันจะได้รับการอ้างอิงถึงชื่อทั้งหมดที่กำหนดmymoduleจากภายในmymoduleตัวเองได้อย่างไร?

สิ่งนี้:

# mymodule.py
names = dir(__thismodule__)

โปรดตรวจสอบstackoverflow.com/questions/3281300/…
ksridhar

คำตอบ:


135

เพียงใช้ globals ()

globals () - ส่งคืนพจนานุกรมที่แสดงถึงตารางสัญลักษณ์สากลปัจจุบัน นี่คือพจนานุกรมของโมดูลปัจจุบันเสมอ (ภายในฟังก์ชันหรือเมธอดนี่คือโมดูลที่กำหนดไว้ไม่ใช่โมดูลที่เรียกใช้)

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


4
มีวิธีใดบ้างในการเข้าถึง gloabals () ของโมดูลการโทรแทนที่จะเป็นโมดูลการกำหนด?
dimo414

9
คุณอาจลองรับ globals ของผู้โทรจากโมดูล traceback ( docs.python.org/library/traceback.html ) แต่กำลังเข้าสู่ดินแดนเวทมนตร์แห่งความมืด ฉันไม่รู้ว่าคุณกำลังพยายามทำอะไรอยู่ แต่คุณอาจต้องการทบทวนการออกแบบของคุณใหม่หากคุณต้องการสิ่งนั้น
Maciej Pasternacki

กรณีคลาสสิกของ "ฉันต้องการ X (เพื่อให้ Y เสร็จ) -> คุณไม่จำเป็นต้องใช้ X คุณต้องใช้ Z" ฉันต้องการ X! ไม่มีความผิดฉันแค่พบว่ามันน่าขบขันและคำตอบที่ได้รับการโหวตมากที่สุดก็ให้คำตอบที่ฉันต้องการ :)
pawamoy

สิ่งสำคัญที่ต้องสังเกตว่า globals () สามารถส่งคืนผลลัพธ์ที่ไม่ถูกต้องได้เนื่องจากขึ้นอยู่กับบริบทที่ถูกเรียก ตัวอย่างเช่นหากโทรออกจากฟังก์ชันคลาสมันจะส่งคืนบริบทส่วนกลางที่เชื่อมโยงกับคลาสไม่ใช่บริบทโมดูลปัจจุบันซึ่งเป็นสิ่งที่แตกต่างกันอย่างมีนัยสำคัญ แม้ว่าจะโทรออกจากฟังก์ชันฟรีก็สามารถส่งคืนบริบทส่วนกลางของโมดูลที่แตกต่างกันได้ขึ้นอยู่กับวิธีการนำเข้าฟังก์ชัน
Andry

163

ตามที่กล่าวไว้ก่อนหน้านี้ globals จะให้พจนานุกรมตรงข้ามกับ dir () ซึ่งจะให้รายชื่อที่กำหนดไว้ในโมดูล วิธีที่ฉันมักจะเห็นสิ่งนี้คือ:

import sys
dir(sys.modules[__name__])

2
ฉันจะเพิ่มความคิดเห็นว่าสิ่งนี้ใช้ไม่ได้กับโมดูล ' หลัก ' (ซึ่งเป็นสิ่งที่เรียกว่าโมดูลที่เรียกใช้ที่เทอร์มินัล) เพราะดูเหมือนจะไม่อยู่ในรายการ sys.modules - แต่มันใช้งานได้จริง :)
markm

อย่างไรก็ตามดูเหมือนว่าจะไม่ทำงานจาก ipdb (ใส่ "import ipdb; ipdb.set_trace ()" ลงในไฟล์ของคุณ)
gatoatigrado

9
ยอดเยี่ยม เพียงแค่นี้ก็อนุญาตให้ฉันไปใช้ docstring โมดูลปัจจุบันเป็นข้อความการใช้งาน sys.modules[__name__].__doc__-
จอร์จ

และเพื่อให้ได้รับความแฮ็กสุด ๆ operators.attrgetter('module.attribute')(sys.modules[__name__])- คุณรู้ไหมถ้าคุณทำเรื่องบ้าๆที่คนอื่นบอกคุณว่าอย่าทำเช่นการนำเข้าแพ็กเกจจากสตริงแบบไดนามิกแล้วทำการปะลิงโดยที่ไม่ได้อยู่ในคลาสหรืออะไรก็ตาม ...
casey

2
สำหรับใครก็ตามที่อ่าน comment โดย george: sys.modules[__name__].__doc__== __doc__ตามที่กำหนดไว้ในเนมสเปซปัจจุบัน การดึงอ็อบเจ็กต์โมดูลเพื่อเข้าถึงแอ็ตทริบิวต์ของตัวเองจึงไม่จำเป็น
Oliver Bestwalter

1

อาจจะตอบช้า แต่ฉันไม่พบคำตอบที่ถูกต้องสำหรับตัวเอง โซลูชันที่ใกล้เคียงและแม่นยำที่สุด (เร็วกว่าinspect.stack()) ใน 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

ข้อดี :

  • แม่นยำกว่าglobals()วิธีการ
  • ไม่ได้ขึ้นอยู่กับเฟรมระดับกลางของสแต็กซึ่งสามารถเพิ่มได้เช่นผ่านการเชื่อมต่อหรือโดยเครื่องมือของบุคคลที่สามเช่น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
  • สามารถจัดการเซสชัน python piped หรือโต้ตอบได้

จุดด้อย:

  • ชนิดที่แม่นยำมากและสามารถส่งคืนโมดูลที่ลงทะเบียนในปฏิบัติการได้เช่นpytest.exeซึ่งอาจไม่ใช่สิ่งที่คุณต้องการ
  • inspect.getmodule ยังอาจส่งคืนไม่มีในโมดูลที่ถูกต้องขึ้นอยู่กับการเชื่อมต่อ

ฉันมีส่วนขยายไปยัง python: จะนำเข้าโมดูลที่กำหนดเส้นทางแบบเต็มได้อย่างไร

ส่วนขยายที่มีฟังก์ชัน wrapper สำหรับกรณีนั้น:

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 ...
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.