sys.builtin_module_names,该解释器中内置模块名称的元组,即包含所有的被编译进Python解释器的模块
sys.copyright,与该解释器有关的版权声明
sys.exec_prefix,提供特定域的目录前缀字符串,用于查找特定机器的python库,该目录中安装了与平台相关的Python文件
sys.executable,解释器的二进制可执行文件的绝对路径,仅在部分系统中此值有意义。如果无法获取其可执行文件的真实路径,则将为空字符串或None
sys.float_info,带有关于float实现所需信息的具名元组。是关于精度和内部表示的底层信息,这些值与标准头文件 float.h 中为 C 语言定义的各种浮点常量对应,具体参考sys.float_info
sys.float_repr_style,反映 repr() 函数在浮点数上行为的字符串。 Python 3.1 及更高版本中,该字符串是 ‘short’,那么对于(非无穷的)浮点数 x,repr(x) 将会生成一个短字符串,满足 float(repr(x)) == x 的特性;否则 float_repr_style 的值将是 ‘legacy’,此时 repr(x) 的行为方式将与 Python 3.1 之前的版本相同
sys.hash_info,带有哈希算法信息的具名元组,给出数字类型的哈希的实现参数。具体参考sys.hash_info
sys.implementation,包含当前运行的 Python 解释器实现信息的对象
sys.int_info,包含Python 内部整数表示形式信息的具名元组
sys.maxsize,容器的最大支持长度,表示 Py_ssize_t 类型的变量可以取到的最大值。在 32 位平台上通常为 2**31 - 1,在 64 位平台上通常为 2**63 - 1
sys.platform,平台标识符。例如,该标识符可用于将特定平台的组件追加到 sys.path 中
sys.prefix,用于查找python库的前缀,即python的安装位置
sys.thread_info,带有关于线程(thread)实现所需信息的具名元组
sys.version,包含 Python 解释器版本号加编译版本号以及所用编译器等额外信息的字符串,此字符串会在交互式解释器启动时显示。注意,不应从该字符串中提取版本信息,而应使用 version_info 以及 platform 模块所提供的函数
sys.version_info,具名元组形式显示的版本信息
l = [sys.builtin_module_names, sys.copyright, sys.exec_prefix, sys.executable, sys.float_info, sys.float_repr_style,
sys.hash_info, sys.implementation,sys.int_info,sys.maxsize,sys.platform,sys.prefix, sys.thread_info,sys.version,sys.version_info]
for i in l:
print(i)
print('*'*10)
('_abc', '_ast', '_codecs', '_collections', '_functools', '_imp', '_io', '_locale', '_operator', '_peg_parser', '_signal', '_sre', '_stat', '_string', '_symtable', '_thread', '_tracemalloc', '_warnings', '_weakref', 'atexit', 'builtins', 'errno', 'faulthandler', 'gc', 'itertools', 'marshal', 'posix', 'pwd', 'sys', 'time', 'xxsubtype')
**********
Copyright (c) 2001-2020 Python Software Foundation.
All Rights Reserved.
Copyright (c) 2000 BeOpen.com.
All Rights Reserved.
Copyright (c) 1995-2001 Corporation for National Research Initiatives.
All Rights Reserved.
Copyright (c) 1991-1995 Stichting Mathematisch Centrum, Amsterdam.
All Rights Reserved.
**********
/opt/miniconda3/envs/py39
**********
/opt/miniconda3/envs/py39/bin/python3
**********
sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)
**********
short
**********
sys.hash_info(width=64, modulus=2305843009213693951, inf=314159, nan=0, imag=1000003, algorithm='siphash24', hash_bits=64, seed_bits=128, cutoff=0)
**********
namespace(name='cpython', cache_tag='cpython-39', version=sys.version_info(major=3, minor=9, micro=0, releaselevel='final', serial=0), hexversion=50921712, _multiarch='darwin')
**********
sys.int_info(bits_per_digit=30, sizeof_digit=4)
**********
9223372036854775807
**********
darwin
**********
/opt/miniconda3/envs/py39
**********
sys.thread_info(name='pthread', lock='mutex+cond', version=None)
**********
3.9.0 (default, Nov 15 2020, 06:25:35)
[Clang 10.0.0 ]
**********
sys.version_info(major=3, minor=9, micro=0, releaselevel='final', serial=0)
**********
(base) 192:~ yxt$ cat fruits.py
import sys
print("this fruit is apple")
print(sys.argv)
(base) 192:~ yxt$ python fruits.py
this fruit is apple
['fruits.py']
(base) 192:~ yxt$
(base) 192:~ yxt$ python -c "print('hello')"
hello
(base) 192:~ yxt$ python -c "import sys;print(sys.argv)"
['-c']
(base) 192:~ yxt$
(base) 192:~ yxt$ cat fruits.py
import sys
print(sys.argv)
if __name__ == "__main__":
print("this is fruits module")
(base) 192:~ yxt$ python -m fruits
['/Users/yxt/fruits.py']
this is fruits module
(base) 192:~ yxt$
>>> import sys
>>> sys.modules
{'sys': , 'builtins': , ...}
(base) 192:~ yxt$ env | grep "PYTHONPATH"
(base) 192:~ yxt$ vim .bash_profile
(base) 192:~ yxt$ source .bash_profile
(base) 192:~ yxt$ env | grep "PYTHONPATH"
PYTHONPATH=/Users/yxt/Desktop
(base) 192:~ yxt$ cat fruits.py
import sys
print(sys.path)
sys.path.append("/Users/yxt/Desktop/play")
print(sys.path)
(base) 192:~ yxt$ python fruits.py
['/Users/yxt', '/Users/yxt/Desktop', '/opt/miniconda3/lib/python39.zip', '/opt/miniconda3/lib/python3.9', '/opt/miniconda3/lib/python3.9/lib-dynload', '/opt/miniconda3/lib/python3.9/site-packages']
['/Users/yxt', '/Users/yxt/Desktop', '/opt/miniconda3/lib/python39.zip', '/opt/miniconda3/lib/python3.9', '/opt/miniconda3/lib/python3.9/lib-dynload', '/opt/miniconda3/lib/python3.9/site-packages', '/Users/yxt/Desktop/play']
(base) 192:~ yxt$
>>> import sys
>>> sys.path
['', '/Users/yxt/Desktop', '/opt/miniconda3/lib/python39.zip', '/opt/miniconda3/lib/python3.9', '/opt/miniconda3/lib/python3.9/lib-dynload', '/opt/miniconda3/lib/python3.9/site-packages']
import sys
f = open(file='file.txt', mode='a')
print(f, type(f))
print(sys.stdout, type(sys.stdout))
f.close()
<_io.TextIOWrapper name='file.txt' mode='a' encoding='UTF-8'>
<_io.TextIOWrapper name='' mode='w' encoding='utf-8'>
def print(self, *args, sep=' ', end='\n', file=None): # known special case of print
"""
print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
Prints the values to a stream, or to sys.stdout by default.
Optional keyword arguments:
file: a file-like object (stream); defaults to the current sys.stdout.
sep: string inserted between values, default a space.
end: string appended after the last value, default a newline.
flush: whether to forcibly flush the stream.
"""
pass
def input(*args, **kwargs): # real signature unknown
"""
Read a string from standard input. The trailing newline is stripped.
The prompt string, if given, is printed to standard output without a
trailing newline before reading input.
If the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise EOFError.
On *nix systems, readline is used if available.
"""
pass
v = 2 * 3 / 0.5 # 表达式
print(v)
sys.stdout = open(file='file.txt', mode='a') # 重定向stdout到文件对象
print('sys') # 输出到文件对象
sys.stdout.close()
sys.stdout = sys.__stdout__ # stdout重定向到将初始值
import sys
s = sys.stdin
print(s, type(s))
<_io.TextIOWrapper name='' mode='r' encoding='utf-8'>
def input(*args, **kwargs): # real signature unknown
"""
Read a string from standard input. The trailing newline is stripped.
The prompt string, if given, is printed to standard output without a
trailing newline before reading input.
If the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise EOFError.
On *nix systems, readline is used if available.
"""
pass
import sys
a = sys.stdin.readline() # 实现标准输入
print(a, type(a)) # 读取标准输入所有内容,包括换行符
b = input('输入内容:') # input函数实现标准输入,即input从标准输入读取内容
print(b) # 标准输入所有内容,但去除尾部换行符
print(f'sys.stdin.readline()读取内容长度为{len(a)}, input读取内容长度为{len(b)}')
ab # 标准输入
ab
输入内容:ab # input()输入
ab
sys.stdin.readline()读取内容长度为3, input读取内容长度为2 # 字符串长度不同
import sys
a = sys.stdin.read()
# a = sys.stdin.read().splitlines() # 返回列表,元素为每行内容且不含每行尾部换行符
print(a, len(a))
ab # 标准输入
cd^D # 标准输入。没有换行,以command+D结束
ab
3 # 输出存在换行,但没有最后的内容
ab # 标准输入
cd # 标准输入。存在换行
^D # 以command+D结束
ab
cd # 读取最后一行内容成功
6
import sys
a = sys.stdin.readlines()
print(a)
ab # 标准输入
cd^D # 标准输入。没有换行,直接以command+D结束
['ab\n'] # 列表,每行内容为元素,读出换行符
ab # 标准输入
cd # 标准输入。存在换行
^D # 以command+D结束
['ab\n', 'cd\n'] # 列表,元素为每行内容(包括换行符),换行符会读出
import sys
stderr = sys.stderr
print(stderr, type(stderr))
<_io.TextIOWrapper name='' mode='w' encoding='utf-8'>
import sys
sys.stderr = open(file='file.txt', mode='w') # 将stderr重定向到另一个文件对象
a = 3 / 0 # 使程序崩溃并将调试信息输出到stderr管道中
sys.stderr.close() # 关闭该文件对象
sys.stderr = sys.__stderr__ # 将标准错文件对象重定向到初始标准错误文件对象
演示的脚本test.py
n = 'a'
r = n + 2
print(n, r)
通过 -i 参数执行脚本,会在运行脚本后进行交互式检查,即打开一个交互式shell窗口
python -i test.py
运行脚本后打开的交互式窗口
(py39) 192:标准库 yxt$ python -i sys模块.py
Traceback (most recent call last):
File "/Users/yxt/Desktop/Exercises/标准库/sys模块.py", line 50, in <module>
r = n + 2
TypeError: can only concatenate str (not "int") to str
>>> sys.last_type, sys.last_value, sys.last_traceback # 异常未处理,已打印错误消息和堆栈回溯,此时 last_type & last_value & last_traceback 存在值
(<class 'TypeError'>, TypeError('can only concatenate str (not "int") to str'), <traceback object at 0x7fe9a9542500>)
>>> import pdb;pdb.pm() # 进入事后调试器
> /Users/yxt/Desktop/Exercises/标准库/sys模块.py(50)<module>()
-> r = n + 2 # 导致程序崩溃的语句
(Pdb) p n # 打印变量n
'a'
(Pdb) q # 退出pdb窗口
>>> quit() # 退出交互式窗口
(py39) 192:标准库 yxt$
import sys, time
if sys.version_info.major == 2: # Python 2
import thread
else: # Python 3
import _thread as thread
lock1 = thread.allocate_lock() # 资源R1
lock2 = thread.allocate_lock() # 资源R2
def thread_entry_A(): # 线程A的入口函数
global lock1, lock2
print("Thread A: Before lock1 Acquire")
lock1.acquire() # 得到资源R1
print("Thread A: After lock1 Acquire")
time.sleep(3)
print("Thread A: Before lock2 Acquire")
lock2.acquire() # 申请资源R2,死锁在这里
print("Thread A: After lock2 Acquire")
lock1.release() # 释放资源R1
lock2.release() # 释放资源R2
def thread_entry_B(): # 线程B的入口函数
global lock1, lock2
print("Thread B: Before lock2 Acquire")
lock2.acquire() # 得到资源R2
print("Thread B: After lock2 Acquire")
time.sleep(3)
print("Thread B: Before lock1 Acquire")
lock1.acquire() # 申请资源R1,死锁在这里
print("Thread B: After lock1 Acquire")
lock1.release() # 释放资源R1
lock2.release() # 释放资源R2
def start_threads():
thread.start_new_thread(thread_entry_A, tuple())
thread.start_new_thread(thread_entry_B, tuple())
time.sleep(5)
for i, frame in sys._current_frames().items():
print(f'线程标识符 {i}, 栈顶帧中的文件名 {frame.f_code.co_filename}、函数名 {frame.f_code.co_name}、行号 {frame.f_lineno}')
print("Main Thread Quit") # 主线程退出,进程也退出
if __name__ == '__main__':
start_threads()
Thread A: Before lock1 Acquire
Thread A: After lock1 Acquire
Thread B: Before lock2 Acquire
Thread B: After lock2 Acquire
Thread B: Before lock1 AcquireThread A: Before lock2 Acquire
线程标识符 123145400258560, 栈顶帧中的文件名 /Users/yxt/Desktop/Exercises/标准库/test.py、函数名 thread_entry_B、行号 31
线程标识符 123145383469056, 栈顶帧中的文件名 /Users/yxt/Desktop/Exercises/标准库/test.py、函数名 thread_entry_A、行号 18
线程标识符 4643649024, 栈顶帧中的文件名 /Users/yxt/Desktop/Exercises/标准库/test.py、函数名 start_threads、行号 42
Main Thread Quit
import sys
def func1():
print('ok')
def func2():
frame = sys._getframe()
print(f'调用栈顶部帧对象:{frame}, 文件名:{frame.f_code.co_filename}, 函数名:{frame.f_code.co_name}, 行号:{frame.f_lineno}')
frame = sys._getframe(1) # 从栈顶向下的深度
print(
f'堆栈栈栈顶向下第二个帧对象:{frame}, 文件名:{frame.f_code.co_filename}, 函数:{frame.f_code.co_name}, 行号:{frame.f_lineno}')
func1()
def func3():
func2()
def func4():
func3()
func4()
调用栈顶部帧对象:, 文件名:/Users/yxt/Desktop/Exercises/标准库/test.py, 函数名:func2, 行号:10
堆栈栈栈顶向下第二个帧对象:, 文件名:/Users/yxt/Desktop/Exercises/标准库/test.py, 函数:func3, 行号:18
ok
import os
os.environ['PYTHONBREAKPOINT'] = '0'
print('run')
breakpoint() # 该断点处无操作
print('finish')
import os
os.environ['PYTHONBREAKPOINT'] = ''
print('run')
breakpoint() # 进入pdb.set_trace()调试器
print('finish')
# debugFunc.py文件
import sys, time
def func(*arg, **kwargs):
while True:
time.sleep(0.25)
s = input('请输入:\n')
if s not in ['a', 'A', 'k', 'K', 'q', 'Q']:
print('请输入"a"查看位置参数、"k"来查看关键字参数、"q"退出调试器', file=sys.stderr)
elif s in ['a', 'A']:
print(arg)
elif s in ['k', 'K']:
print(kwargs)
elif s in ['q', 'Q']:
break
import os
def f():
os.environ['PYTHONBREAKPOINT'] = '标准库.debugFunc.func' # 调用debugFunc模块的func函数
# os.environ['PYTHONBREAKPOINT'] = '标准库.debugFunc.func2' # func2不存在,发生报错并忽略断点
breakpoint('1', '2', '3', v='6')
print('end')
f()
请输入:
l
请输入"a"查看位置参数、"k"来查看关键字参数、"q"退出调试器
请输入:
a
('1', '2', '3')
请输入:
k
{'v': '6'}
请输入:
q
end
def displayhook(value):
if value is None:
return
# Set '_' to None to avoid recursion
builtins._ = None
text = repr(value) # 将value转为规范字符串表现形式
try:
sys.stdout.write(text) # 写入标准输入
except UnicodeEncodeError: # Unicode编码错误,可能是sys.stdout.errors错误处理方案为'strict',所以遇到编码错误时抛出该异常
bytes = text.encode(sys.stdout.encoding, 'backslashreplace') # 将字符串编码,错误处理方案设置为'backslashreplace',即使用反斜杠(\)开始的转义字符名称序列来替换未能转换的字符
if hasattr(sys.stdout, 'buffer'): # 若有底层二进制'buffer'对象,则在标准流写入二进制数据
sys.stdout.buffer.write(bytes)
else: # 将编码数据解码,并写入标准流
text = bytes.decode(sys.stdout.encoding, 'strict')
sys.stdout.write(text)
sys.stdout.write("\n")
builtins._ = value
>>> import sys
>>> def displayhook2(value):
text = '规范字符串:' + repr(value)
sys.stdout.write(text)
sys.stdout.write("\n")
...
>>> sys.displayhook = displayhook2
>>> 2 + 3
规范字符串:5
是什么:一个处理任何未捕获异常(除SystemExit之外的)的函数,用于将所给的回溯与异常输出到stderr
def excepthook(*args, **kwargs): # real signature unknown
""" Handle an exception by displaying it with a traceback on sys.stderr. """
pass
传入参数:type是正在处理的异常类(它是 BaseException 的子类),value 是异常实例(异常类型的实例),traceback 是一个回溯对象,该对象封装了最初发生异常时的调用堆栈
import sys
def p():
return 1 / 0
def my_excepthook(ttype, value, traceback):
print("异常类型:{}, {}".format(ttype, type(ttype)))
print("异常实例:{}, {}".format(value, type(value)))
print("回溯对象:{}, {}, {}".format(traceback, type(traceback), dir(traceback)))
sys.excepthook = my_excepthook # 自定义函数处理顶级异常
p()
异常类型:,
异常实例:division by zero,
回溯对象:, , ['tb_frame', 'tb_lasti', 'tb_lineno', 'tb_next']
函数调用:当程序抛出一个未被捕获的异常时,解释器会调用excepthook函数,将其获得的回溯与异常信息输出到stderr。在交互式会话中,这会在控制权返回到提示符之前发生;在 Python 程序中,这会在程序退出之前发生
自定义该类顶级异常处理过程:可以将接受3个参数的函数赋给 sys.excepthook。自定义函数处理traceback对象输出异常信息可行但比较麻烦,输出信息可读性较差;可使用traceback模块内置的辅助函数来提取异常信息。以下为异常在一系列嵌套较深的函数调用中引发,函数在不同模块
# module2.py
def m():
return 1 / 0
def n():
m()
import sys
from module2 import n
def p():
n()
def myExcepthook(ttype, value, traceback):
print("异常类型:{}".format(ttype))
print("异常实例:{}".format(value))
i = 1
while traceback: # 链表结构
print("第{}层堆栈信息".format(i))
print('异常追踪行数:{}'.format(traceback.tb_lineno))
tracebackCode = traceback.tb_frame.f_code # 异常信息位置
print("文件名:{}".format(tracebackCode.co_filename))
print("函数或者模块名:{}".format(tracebackCode.co_name))
traceback = traceback.tb_next # 下一个回溯对象
i += 1
sys.excepthook = myExcepthook
p()
异常类型:
异常实例:division by zero
第1层堆栈信息
异常追踪行数:24
文件名:/Users/yxt/Desktop/Exercises/标准库/sys模块.py
函数或者模块名:
第2层堆栈信息
异常追踪行数:6
文件名:/Users/yxt/Desktop/Exercises/标准库/sys模块.py
函数或者模块名:p
第3层堆栈信息
异常追踪行数:6
文件名:/Users/yxt/Desktop/Exercises/标准库/module2.py
函数或者模块名:n
第4层堆栈信息
异常追踪行数:2
文件名:/Users/yxt/Desktop/Exercises/标准库/module2.py
函数或者模块名:m
import sys
f = lambda x: x / 0
try:
f(5)
except Exception as e:
print(e, type(e))
division by zero
import sys
f = lambda x: x / 0
try:
f(5)
except Exception as e:
ttype, tvalue, ttraceback = sys.exc_info()
print(ttype, tvalue, ttraceback)
while ttraceback:
print('异常追踪行数:{}'.format(ttraceback.tb_lineno))
tracebackCode = ttraceback.tb_frame.f_code
print("文件名:{}".format(tracebackCode.co_filename))
print("函数或者模块名:{}".format(tracebackCode.co_name))
ttraceback = ttraceback.tb_next
division by zero
文件名:/Users/yxt/Desktop/Exercises/标准库/sys模块.py, 函数或者模块名:, 异常追踪行数:5
文件名:/Users/yxt/Desktop/Exercises/标准库/sys模块.py, 函数或者模块名:, 异常追踪行数:3
import sys, traceback
f = lambda x: x / 0
try:
f(5)
except Exception as e:
ttype, tvalue, ttraceback = sys.exc_info()
print(ttype, tvalue, ttraceback)
traceback.print_tb(ttraceback)
division by zero
File "/Users/yxt/Desktop/Exercises/标准库/sys模块.py", line 5, in
f(5)
File "/Users/yxt/Desktop/Exercises/标准库/sys模块.py", line 3, in
f = lambda x: x / 0
import traceback
f = lambda x: x / 0
try:
f(5)
except Exception as e:
traceback.print_exc()
Traceback (most recent call last):
File "/Users/yxt/Desktop/Exercises/标准库/sys模块.py", line 5, in
f(5)
File "/Users/yxt/Desktop/Exercises/标准库/sys模块.py", line 3, in
f = lambda x: x / 0
ZeroDivisionError: division by zero
import sys
# 不传入、传入0、传入None,都等同于传入0,代表 成功终止
sys.exit()
sys.exit(0)
sys.exit(None)
# 整数类型非0(1-127),代表 异常终止
sys.exit(1)
# 会将传入参数打印至标准错误,且退出代码为 1
sys.exit('some error message')
import sys
try:
print('run')
sys.exit('some error message')
except Exception as e: # 不能捕获SystemExit异常,在执行清理处理程序后(finally 子句),退出解释器
print(e)
print('exception class')
finally:
print('end')
print('out') # 不会执行该行
run
end
some error message
import sys
try:
print('run')
sys.exit('some error message')
except SystemExit as e: # 捕获SystemExit异常,执行清理处理程序后,正常运行后面程序
print(e)
print('SystemExit:基类为BaseException')
except Exception as e:
print(e)
print('exception class')
finally:
print('end')
print('out')
run
some error message
SystemExit:基类为BaseException
end
out
from __future__ import print_function
from sys import getsizeof, stderr
from itertools import chain
from collections import deque
try:
from reprlib import repr
except ImportError:
pass
def total_size(o, handlers={}, verbose=False):
dict_handler = lambda d: chain.from_iterable(d.items())
all_handlers = {tuple: iter,
list: iter,
deque: iter,
dict: dict_handler,
set: iter,
frozenset: iter,
}
all_handlers.update(handlers) # user handlers take precedence
seen = set() # track which object id's have already been seen
default_size = getsizeof(0) # estimate sizeof object without __sizeof__
def sizeof(o):
if id(o) in seen: # do not double count the same object
return 0
seen.add(id(o))
s = getsizeof(o, default_size)
if verbose:
print(s, type(o), repr(o), file=stderr)
for typ, handler in all_handlers.items():
if isinstance(o, typ):
s += sum(map(sizeof, handler(o))) # 处理每个容器的元素、对象的属性
break
return s
return sizeof(o)
class userClass(object):
def __init__(self):
self.integer = 1
self.integer2 = 20
self.array = [['sd', 'ds'], 32]
def myHandler(obj): # 将对象所有属性变为迭代器并返回
if not hasattr(obj.__class__, '__slots__'): # 当对象不包含 __slots__时,通常意味着包含__dict__,但一些特殊的内置类两者都没有(表示sys.getsizeof()事实上返回了正确的值)
if hasattr(obj, '__dict__'):
return iter([i for i in obj.__dict__.values()])
return []
return iter([getattr(obj, i) for i in obj.__class__.__slots__ if hasattr(obj, i)]) # 根据类中定义的__slots__,获取实例中存在属性
myClass = userClass()
handlers = {userClass: myHandler}
d = dict(a=1, b=2, c=3, d=[4, 5, 6, 7], e='a string of chars', k=myClass)
print(total_size(d, handlers=handlers, verbose=True))
>>> import sys
>>> a = sys.intern('why do pangolins dream of quiche')
>>> b = sys.intern('why do pangolins dream of quiche') # 不会重新创建,返回interned表中插入的字符串
>>> b is a
True
>>> c = 'why do pangolins dream of quiche'
>>> c is a
False
>>>
import sys
CALL_EVENT_LIST = []
class FileFilter():
"""
tracefunc会处理每一次函数的调用和退出,包括第三方库和python内置库里的函数,这会导致搜集的信息过多。
对当前调用栈的函数所在文件名称,通过此类函数筛选指定区域的代码
"""
@staticmethod
def filter(filename):
return True
@staticmethod
def old_filter(filename):
return True
def tracefunc(frame, event, arg):
if event == "call":
tracefunc.stack_level += 1
unique_id = frame.f_code.co_filename + str(frame.f_lineno)
if unique_id in tracefunc.memorized:
return
# Part of filename MUST be in white list.
if 'self' in frame.f_locals:
class_name = frame.f_locals['self'].__class__.__name__
func_name = class_name + '.' + frame.f_code.co_name
else:
func_name = frame.f_code.co_name
func_name = '{indent}{name}'.format(
indent=tracefunc.stack_level * 2 * '-', name=func_name)
if not FileFilter.filter(frame.f_code.co_filename):
return
frame_back = frame.f_back # 获取调用函数时的信息
txt = '{: <40} # {}, {}, {}, {}'.format(
func_name, frame.f_code.co_filename, frame.f_lineno, frame_back.f_code.co_filename, frame_back.f_lineno)
CALL_EVENT_LIST.append(txt)
tracefunc.memorized.add(unique_id)
elif event == "return":
tracefunc.stack_level -= 1
def start(filter_func=None): # 设置过滤器、性能分析函数
if filter_func:
FileFilter.filter = filter_func
tracefunc.memorized = set() # 同一行代码多次调用时,只记录第一次调用
tracefunc.stack_level = 0 # 标注函数调用层数
CALL_EVENT_LIST.clear()
sys.setprofile(tracefunc) # 设置性能函数
def output(): # 输出函数调用过程
for text in CALL_EVENT_LIST:
print(text)
CALL_EVENT_LIST.clear()
def stop():
def do_nothing(frame, event, arg):
pass
FileFilter.filter = FileFilter.old_filter # 过滤器清空
sys.setprofile(do_nothing) # 关闭性能分析函数
def func1():
print('ok')
def func2():
func1()
def func3():
func2()
def func4():
func3()
def file_filter(filename):
if filename.find('calc') != -1:
return True
return False
start(filter_func=lambda x: x.find(__file__) != -1) # lambda函数为过滤函数
func4()
stop()
output()
ok
func4 # /Users/yxt/Desktop/Exercises/标准库/sys模块.py, 91, /Users/yxt/Desktop/Exercises/标准库/sys模块.py, 102
--func3 # /Users/yxt/Desktop/Exercises/标准库/sys模块.py, 87, /Users/yxt/Desktop/Exercises/标准库/sys模块.py, 92
----func2 # /Users/yxt/Desktop/Exercises/标准库/sys模块.py, 83, /Users/yxt/Desktop/Exercises/标准库/sys模块.py, 88
------func1 # /Users/yxt/Desktop/Exercises/标准库/sys模块.py, 79, /Users/yxt/Desktop/Exercises/标准库/sys模块.py, 84
stop # /Users/yxt/Desktop/Exercises/标准库/sys模块.py, 71, /Users/yxt/Desktop/Exercises/标准库/sys模块.py, 103
>>> import sys
>>> sys.getrecursionlimit()
1000
是什么
传入参数:frame 是当前的堆栈帧,event 是一个字符串(事件名),包括’call’、‘line’、‘return’、‘exception’ 或 ‘opcode’,arg 取决于事件类型
event | 含义 | arg | 返回值 | 注意 |
---|---|---|---|---|
call | 表示调用了某个函数(或进入了其他的代码块),全局跟踪函数将被调用 | None | 返回值将指定局部跟踪函数 | |
line | 表示解释器即将执行新一行代码或重新执行循环条件,局部跟踪函数将被调用 | None | 返回值将指定新的局部跟踪函数 | 工作原理详参见 Objects/lnotab_notes.txt。在该堆栈帧禁用每行触发事件,frame.f_trace_lines=False |
return | 表示某个函数(或别的代码块)即将返回,局部跟踪函数将被调用 | arg 是即将返回的值 | 跟踪函数的返回值将被忽略 | 若返回事件是由于抛出异常,则arg为None |
exception | 表示发生了某个异常,局部跟踪函数将被调用 | arg 是一个 (exception, value, traceback) 元组 | 返回值将指定新的局部跟踪函数 | 由于异常是在链式调用中传播的,所以每一级都会产生一个 ‘exception’ 事件 |
opcode | 表示解释器即将执行一个新的操作码,局部跟踪函数将被调用 | None | 返回值将指定新的局部跟踪函数 | 每操作码触发事件默认情况下都不发出:必须在堆栈帧上将 f_trace_opcodes 显式地设置为 True 来请求这些事件 |
全局跟踪函数与局部跟踪函数:当发生call事件时,会调用全局跟踪函数(即设置的跟踪函数),其返回值将指定局部跟踪函数。当发生其他事件时,会调用局部跟踪函数,其返回值应为局部跟踪函数自身的引用(或对另一个函数的引用)。如果跟踪函数返回None,代表不需要跟踪当前的作用范围或停止跟踪起作用范围。如果跟踪函数出错,则该跟踪函数将被取消设置,类似于调用 settrace(None)
import sys
# 全局跟踪函数(call事件),本地跟踪函数也是该函数(其他事件)
def my_tracer(frame, event, arg=None):
s = 'global:' if event == 'call' else 'local:'
s += f"堆栈帧地址 {id(frame)}, 传入参数 {arg},事件 {event} 发生在 {frame.f_code.co_name} 第{frame.f_lineno}行"
print(s)
return my_tracer
def f2():
return "GFG"
def f1():
return f2()
sys.settrace(my_tracer)
f1()
global:堆栈帧地址 140662066957776, 传入参数 None,事件 call 发生在 f1 第16行
local:堆栈帧地址 140662066957776, 传入参数 None,事件 line 发生在 f1 第17行
global:堆栈帧地址 140662067273280, 传入参数 None,事件 call 发生在 f2 第12行
local:堆栈帧地址 140662067273280, 传入参数 None,事件 line 发生在 f2 第13行
local:堆栈帧地址 140662067273280, 传入参数 GFG,事件 return 发生在 f2 第13行
local:堆栈帧地址 140662066957776, 传入参数 GFG,事件 return 发生在 f1 第17行
显式设置局部跟踪函数:在堆栈帧上通过frame.f_trace = tracefunc 来设置跟踪函数,而不是用现有跟踪函数的返回值去间接设置它。为了使上述设置起效,当前帧上的跟踪函数必须激活,使用 settrace() 来设置全局跟踪函数才能启用运行时跟踪机制。关于代码对象和帧对象的更多信息请参考 标准类型层级结构
import sys
def local_trace_function(frame, event, arg): # 局部跟踪函数
print(f"local:堆栈帧地址 {id(frame)}, 传入参数 {arg},事件 {event} 发生在 {frame.f_code.co_name} 第{frame.f_lineno}行")
def global_trace_function(frame, event, arg): # 全局跟踪函数
print(f"global:堆栈帧地址 {id(frame)}, 传入参数 {arg},事件 {event} 发生在 {frame.f_code.co_name} 第{frame.f_lineno}行")
frame.f_trace = local_trace_function # 显式设置局部跟踪函数到堆栈帧对象
def f2():
return "GFG"
def f1():
return f2()
sys.settrace(global_trace_function)
f1()
global:堆栈帧地址 140574221454800, 传入参数 None,事件 call 发生在 f1 第17行
local:堆栈帧地址 140574221454800, 传入参数 None,事件 line 发生在 f1 第18行
global:堆栈帧地址 140574203098960, 传入参数 None,事件 call 发生在 f2 第13行
local:堆栈帧地址 140574203098960, 传入参数 None,事件 line 发生在 f2 第14行
local:堆栈帧地址 140574203098960, 传入参数 GFG,事件 return 发生在 f2 第14行
local:堆栈帧地址 140574221454800, 传入参数 GFG,事件 return 发生在 f1 第18行
多线程情况下使用threading.settrace(func)设置跟踪函数
import time
import threading
def my_tracer(frame, event, arg=None):
s = f"Passing the trace function and current thread is {str(threading.current_thread().name)}, event is {event}, function is {frame.f_code.co_name}"
print(s, flush=True)
# return my_tracer # 跟踪当前的作用范围(设置局部跟踪函数)
def thread_1(i):
time.sleep(2)
print("Value by Thread-1:", i)
def thread_2(i):
print("Value by Thread-2:", i)
def thread_3(i):
time.sleep(1)
print("Value by Thread-3:", i)
threading.settrace(my_tracer)
thread1 = threading.Thread(target=thread_1, args=(1,))
thread2 = threading.Thread(target=thread_2, args=(2,))
thread3 = threading.Thread(target=thread_3, args=(3,))
thread1.start()
thread2.start()
thread3.start()
Passing the trace function and current thread is Thread-1, event is call, function is run # 线程1,调用函数为run
Passing the trace function and current thread is Thread-1, event is call, function is thread_1 # 线程1,调用函数为thread_1
Passing the trace function and current thread is Thread-2, event is call, function is run
Passing the trace function and current thread is Thread-2, event is call, function is thread_2
Value by Thread-2: 2 # 由于线程2没有IO阻塞,因此直接打印
Passing the trace function and current thread is Thread-3, event is call, function is run
Passing the trace function and current thread is Thread-3, event is call, function is thread_3
Value by Thread-3: 3
Value by Thread-1: 1
注意:settrace() 函数仅用于实现调试器,性能分析器,打包工具等。它的行为是实现平台的一部分,而不是语言定义的一部分,因此并非在所有 Python 实现中都可用
3.7版本更改:添加了 ‘opcode’ 事件类型;为帧对象添加了 f_trace_lines 和 f_trace_opcodes 属性