python中獲取當前位置所在的行號和函數名

對於python,這幾天一直有兩個問題在困擾我:

  • 1.python中沒辦法直接取得當前的行號和函數名。這是有人在論壇裏提出的問題,底下一羣人只是在猜測python爲什麼不像__file__一樣提供__line__和__func__,但是卻最終也沒有找到解決方案。
  • 2.如果一個函數在不知道自己名字的情況下,怎麼才能遞歸調用自己。這是我一個同事問我的,其實也是獲取函數名,但是當時也是回答不出來。

但是今晚!所有的問題都有了答案。
一切還要從我用python的logging模塊說起,logging中的format中是有如下選項的:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
%(name)s            Name of the logger (logging channel)
%(levelno)s         Numeric logging level for the message (DEBUG, INFO,
                    WARNING, ERROR, CRITICAL)
%(levelname)s       Text logging level for the message ("DEBUG", "INFO",
                    "WARNING", "ERROR", "CRITICAL")
%(pathname)s        Full pathname of the source file where the logging
                    call was issued (if available)
%(filename)s        Filename portion of pathname
%(module)s          Module (name portion of filename)
%(lineno)d          Source line number where the logging call was issued
                    (if available)
%(funcName)s        Function name
%(created)f         Time when the LogRecord was created (time.time()
                    return value)
%(asctime)s         Textual time when the LogRecord was created
%(msecs)d           Millisecond portion of the creation time
%(relativeCreated)d Time in milliseconds when the LogRecord was created,
                    relative to the time the logging module was loaded
                    (typically at application startup time)
%(thread)d          Thread ID (if available)
%(threadName)s      Thread name (if available)
%(process)d         Process ID (if available)
%(message)s         The result of record.getMessage(), computed just as
                    the record is emitted

也就是說,logging是能夠獲取到調用者的行號和函數名的,那會不會也可以獲取到自己的行號和函數名呢?
我們來看一下源碼,主要部分如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
def currentframe():
    """Return the frame object for the caller's stack frame."""
    try:
        raise Exception
    except:
        return sys.exc_info()[2].tb_frame.f_back
def findCaller(self):
    """
    Find the stack frame of the caller so that we can note the source
    file name, line number and function name.
    """
    f = currentframe()
    #On some versions of IronPython, currentframe() returns None if
    #IronPython isn't run with -X:Frames.
    if f is not None:
        f = f.f_back
    rv = "(unknown file)", 0, "(unknown function)"
    while hasattr(f, "f_code"):
        co = f.f_code
        filename = os.path.normcase(co.co_filename)
        if filename == _srcfile:
            f = f.f_back
            continue
        rv = (co.co_filename, f.f_lineno, co.co_name)
        break
    return rv
def _log(self, level, msg, args, exc_info=None, extra=None):
    """
    Low-level logging routine which creates a LogRecord and then calls
    all the handlers of this logger to handle the record.
    """
    if _srcfile:
        #IronPython doesn't track Python frames, so findCaller throws an
        #exception on some versions of IronPython. We trap it here so that
        #IronPython can use logging.
        try:
            fn, lno, func = self.findCaller()
        except ValueError:
            fn, lno, func = "(unknown file)", 0, "(unknown function)"
    else:
        fn, lno, func = "(unknown file)", 0, "(unknown function)"
    if exc_info:
        if not isinstance(exc_info, tuple):
            exc_info = sys.exc_info()
    record = self.makeRecord(self.name, level, fn, lno, msg, args, exc_info, func, extra)
    self.handle(record)

我簡單解釋一下,實際上是通過在currentframe函數中拋出一個異常,然後通過向上查找的方式,找到調用的信息。其中

?
1
rv = (co.co_filename, f.f_lineno, co.co_name)

的三個值分別爲文件名,行號,函數名。(可以去http://docs.python.org/library/sys.html來看一下代碼中幾個系統函數的說明)
OK,如果已經看懂了源碼,那獲取當前位置的行號和函數名相信也非常清楚了,代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/python
# -*- coding: utf-8 -*-
'''
#=============================================================================
#  Author:          dantezhu - http://www.vimer.cn
#  Email:           [email protected]
#  FileName:        xf.py
#  Description:     獲取當前位置的行號和函數名
#  Version:         1.0
#  LastChange:      2010-12-17 01:19:19
#  History:        
#=============================================================================
'''
import sys
def get_cur_info():
    """Return the frame object for the caller's stack frame."""
    try:
        raise Exception
    except:
        f = sys.exc_info()[2].tb_frame.f_back
    return (f.f_code.co_name, f.f_lineno)
  
def callfunc():
    print get_cur_info()
  
  
if __name__ == '__main__':
    callfunc()

輸入結果是:

?
1
('callfunc', 24)

符合預期~~
哈哈,OK!現在應該不用再抱怨取不到行號和函數名了吧~

=============================================================================
後來發現,其實也可以有更簡單的方法,如下:

?
1
2
3
4
5
import sys
def get_cur_info():
    print sys._getframe().f_code.co_name
    print sys._getframe().f_back.f_code.co_name
get_cur_info()

 

 

================================================================================

另外,利用python的 inspect 模塊中的getframeinfo也可以得到.

inspect.getframeinfo( frame [context ])

Get information about a frame or traceback object. A 5-tuple is returned, the last five elements of the frame’s frame record.

Changed in version 2.6: Returns a named tuple Traceback(filename, lineno, function, code_context, index).

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章