對于python,這幾天一直有兩個問題在困擾我:
1.python中沒辦法直接取得當前的行號和函數名。這是有人在論壇里提出的問題,底下一群人只是在猜測python為什么不像__file__一樣提供__line__和__func__,但是卻最終也沒有找到解決方案。
2.如果一個函數在不知道自己名字的情況下,怎么才能遞歸調用自己。這是我一個同事問我的,其實也是獲取函數名,但是當時也是回答不出來。
但是今晚!所有的問題都有了答案。
一切還要從我用python的logging模塊說起,logging中的format中是有如下選項的:
%(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是能夠獲取到調用者的行號和函數名的,那會不會也可以獲取到自己的行號和函數名呢?
我們來看一下源碼,主要部分如下:
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函數中拋出一個異常,然后通過向上查找的方式,找到調用的信息。其中
rv = (co.co_filename, f.f_lineno, co.co_name)
的三個值分別為文件名,行號,函數名。(可以去http://docs.python.org/library/sys.html來看一下代碼中幾個系統函數的說明)
OK,如果已經看懂了源碼,那獲取當前位置的行號和函數名相信也非常清楚了,代碼如下:
#!/usr/bin/python
# -*- coding: utf-8 -*-
'''
#=============================================================================
# FileName: xf.py
# Description: 獲取當前位置的行號和函數名
# Version: 1.0
#=============================================================================
'''
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()
輸入結果是:
('callfunc', 24)
符合預期~~
哈哈,OK!現在應該不用再抱怨取不到行號和函數名了吧~=============================================================================
后來發現,其實也可以有更簡單的方法,如下:
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()
調用結果是:
get_cur_info
<module>