對於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).