Nhận __name__ của việc gọi mô-đun của hàm bằng Python


96

Giả sử myapp/foo.pychứa:

def info(msg):
    caller_name = ????
    print '[%s] %s' % (caller_name, msg)

myapp/bar.pychứa:

import foo
foo.info('Hello') # => [myapp.bar] Hello

Tôi muốn caller_nameđược đặt thành __name__thuộc tính của mô-đun gọi hàm '(là' myapp.foo ') trong trường hợp này. Điều này có thể giải quyết như thế nào?


Giả sử rằng một số gọi điểm vào kịch bản khác bar.py .. và do đó caller_namekhông thể được__main__
Sridhar Ratnakumar

Câu trả lời:


122

Kiểm tra mô-đun kiểm tra:

inspect.stack() sẽ trả về thông tin ngăn xếp.

Bên trong một hàm, inspect.stack()[1]sẽ trả về ngăn xếp của người gọi của bạn. Từ đó, bạn có thể biết thêm thông tin về tên chức năng, mô-đun, v.v. của người gọi.

Xem tài liệu để biết chi tiết:

http://docs.python.org/library/inspect.html

Ngoài ra, Doug Hellmann có một bản viết tốt về mô-đun kiểm tra trong loạt bài PyMOTW của mình:

http://pymotw.com/2/inspect/index.html#module-inspect

CHỈNH SỬA: Đây là một số mã làm những gì bạn muốn, tôi nghĩ:

def info(msg):
    frm = inspect.stack()[1]
    mod = inspect.getmodule(frm[0])
    print '[%s] %s' % (mod.__name__, msg)

1
Vậy làm cách nào để lấy __name__thuộc tính của module này bằng inspectmodule? Ví dụ, làm cách nào để lấy lại myapp.foo(không myapp/foo.py) trong ví dụ trên? Tôi đã thử sử dụng mô-đun kiểm tra trước khi đăng tại SO.
Sridhar Ratnakumar,

6
Hãy lưu ý rằng điều này sẽ tương tác kỳ lạ với các hook nhập khẩu, không hoạt động trên ironpython và có thể hoạt động theo những cách đáng ngạc nhiên trên jython. Tốt nhất là bạn có thể tránh được ma thuật như thế này.
Glyph

2
Cũng lưu ý rằng việc giữ một tham chiếu đến khung ngăn xếp có thể ngăn GC của Python hoạt động chính xác. Xem cảnh báo tại đây: docs.python.org/library/inspect.html#the-interpreter-stack
Kamil Kisiel,

6
Lưu ý rằng nếu chức năng người gọi được trang trí (@ ...), bạn cần phải truy cập inspect.stack()[2]cho người gọi thực.
Amir Ali Akbari

Cũng lưu ý rằng logic này không hoạt động bình thường khi bạn biên dịch mã python của mình thành exe bằng pyinstaller.
panofish

19

Đối mặt với một vấn đề tương tự, tôi nhận thấy rằng sys._current_frames () từ mô-đun sys chứa thông tin thú vị có thể giúp bạn mà không cần phải nhập kiểm tra, ít nhất là trong các trường hợp sử dụng cụ thể.

>>> sys._current_frames()
{4052: <frame object at 0x03200C98>}

Sau đó, bạn có thể "di chuyển lên" bằng cách sử dụng f_back:

>>> f = sys._current_frames().values()[0]
>>> # for python3: f = list(sys._current_frames().values())[0]

>>> print f.f_back.f_globals['__file__']
'/base/data/home/apps/apricot/1.6456165165151/caller.py'

>>> print f.f_back.f_globals['__name__']
'__main__'

Đối với tên tệp, bạn cũng có thể sử dụng f.f_back.f_code.co_filename, theo gợi ý của Mark Roddy ở trên. Tôi không chắc chắn về các giới hạn và lưu ý của phương pháp này (nhiều luồng rất có thể sẽ là một vấn đề) nhưng tôi định sử dụng nó trong trường hợp của mình.


2
lưu ý: mã verify.stack FAILS sau khi biên dịch sang exe bằng pyinstaller, nhưng sử dụng sys._current_frames LÀM VIỆC THẬT ... vì vậy đây là kỹ thuật ưa thích đối với tôi.
panofish

7
Tôi nghĩ rằng sẽ dễ dàng hơn để lấy khung trước đó bằng cách sys._getframe(1)thay vì gọi sys._current_frames()(btw trả về ánh xạ khung cho mọi luồng).
hooblei

Cảm ơn bạn hooblei, tôi chưa thử nghiệm nó nhưng có vẻ rất hữu ích cho các tình huống đa luồng.
Louis LC

Tôi thích sử dụng inspect.currentframe()thay vì sys._current_frames().values()[0].
Aran-Fey

3

Tôi không khuyên bạn nên làm điều này, nhưng bạn có thể hoàn thành mục tiêu của mình bằng phương pháp sau:

def caller_name():
    frame=inspect.currentframe()
    frame=frame.f_back.f_back
    code=frame.f_code
    return code.co_filename

Sau đó cập nhật phương pháp hiện có của bạn như sau:

def info(msg):
    caller = caller_name()
    print '[%s] %s' % (caller, msg)

7
Filename là không giống như__name__
Sridhar Ratnakumar
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.