sys模块常用介绍 - 系统相关的参数和函数

目录

  • 常量
  • 变量
    • sys.argv
    • sys.modules
    • sys.path
    • sys.stdout
    • sys.stdin
    • sys.stderr
    • sys.last_type & sys.last_value & sys.last_traceback
  • 函数
    • sys._current_frames()
    • sys._getframe([depth])
    • sys.breakpointhook()
    • sys.displayhook(value)
    • sys.excepthook(type, value, traceback)
    • sys.exc_info()
    • sys.exit([arg])
    • sys.getallocatedblocks()
    • sys.getrefcount(object)
    • sys.getsizeof(object[, default])
    • sys.intern(string)
    • sys.setprofile(profilefunc) & sys.getprofile()
    • sys.setrecursionlimit(limit) & sys.getrecursionlimit()
    • sys.settrace(tracefunc) & sys.gettrace()

sys模块提供了一些变量和函数,主要与解释器相关。这些变量可能被解释器使用,也可能由解释器提供;这些函数与解释器有很强的关系。本文总结其中常用的变量与函数,其他可参考 sys — 系统相关的参数和函数。本文演示案例的解释器版本为3.9

常量

  • sys.builtin_module_names,该解释器中内置模块名称的元组,即包含所有的被编译进Python解释器的模块

  • sys.copyright,与该解释器有关的版权声明

  • sys.exec_prefix,提供特定域的目录前缀字符串,用于查找特定机器的python库,该目录中安装了与平台相关的Python文件

    • 默认是 ‘/usr/local’
    • 该目录前缀可以在构建时使用 configure 脚本的 --exec-prefix 参数进行设置
    • 所有配置文件(如 pyconfig.h 头文件)都安装在目录 exec_prefix/lib/pythonX.Y/config 中
    • 共享库模块安装在 exec_prefix/lib/pythonX.Y/lib-dynload 中
    • 如果在一个虚拟环境中,该值将在 site.py 中被修改,指向虚拟环境。Python 安装位置仍然可以用 base_exec_prefix 来获取
  • 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)
    **********
    

变量

sys.argv

  • 是什么:一个列表,包含了被传递给 Python 脚本的命令行参数,即解释器读入的所有参数。注意,如果没有脚本名被传递给解释器,argv[0]为空字符串
  • argv第一个元素是什么,包括以下情况
    • 用文件名作为参数执行脚本文件。argv[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$
      
    • 通过-c command执行命令, 可用换行符分隔多条语句。argv[0]被设置为"-c"
      (base) 192:~ yxt$ python -c "print('hello')"
      hello
      (base) 192:~ yxt$ python -c "import sys;print(sys.argv)"
      ['-c']
      (base) 192:~ yxt$
      
    • 通过-m module-name执行模块(即在sys.path中搜索指定模块,并以 __main__ 模块执行其内容)。argv[0]为完整路径名
      (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$ 
      

sys.modules

  • 是什么:一个字典,将模块名称映射到已加载的模块。可以操作该字典来强制重新加载模块,但替换的字典不一定会按预期工作
    >>> import sys
    >>> sys.modules
    {'sys': , 'builtins': , ...}
    

sys.path

  • 是什么:一个字符串列表,用于指定模块的搜索路径。初始化自环境变量PYTHONPATH,加上一个与依赖于安装的默认路径
  • 启动程序时将会初始化该列表,列表第一项path[0]是包含调用python解释器脚本的目录。注意,脚本目录在PYTHONPATH作为条目插入之前插入
    (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$
    
  • 若脚本目录不可用(例如解释器被交互方式调用,或从标准输入中读取脚本),则path[0]为空字符串,这会使python首先搜索当前目录的模块。例如,python交互模式下path[0]为空字符串
    >>> 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']
    

sys.stdout

  • 是什么:stdout是解释器用于标准输出的文件对象。该文件流是常规的文本文件,与open函数返回的对象一致,其参数中的字符编码取决于各个平台
    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'> 
    
  • 如何理解stdout:详参 如何理解python中的sys.stdout和sys.stderr
    • 在C程序中,一般有三个默认打开的“文件”:stdin、stdout和stderr。在C中输入和输出时,默认情况下它们来自stdin和stdout。但也可以在代码需要文件的地方使用它们,或者将它们重新分配为新文件。
    • Python试图“模仿”C的这种行为,当调用print()时,输出文本将被写入sys.stdout;当做input()时,输入来自sys.stdin;异常被写入sys.stderr。
  • 用途:用于print()输出、input()的提示符、expression语句输出
    • print()传入参数file:如果file不存在或为 None,则默认为sys.stdout。即将print()的文本写入sys.stdout,实现解释器的标准输出
      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
      
    • input()提示符:如果input()存在实参(prompt string),则将其写入标准输出(即将提示符写入sys.stdout),末尾不带换行符
      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
      
    • expression语句的输出:表达式是可以求出某个值的语法单元,即一个表达式就是表达元素例如字面值、名称、属性访问、运算符或函数调用的汇总,它们最终都会返回一个值。 Python并非所有语言构件都是表达式,还存在不能被用作表达式的语句,例如 while、赋值等。详参 expression
      v = 2 * 3 / 0.5  # 表达式
      print(v)
      
  • sys.stdout与sys.__stdout__的关系
    • 程序开始时,__stdin__、__stderr__、__stdout__这些对象存有 stdin、stderr 和 stdout 的初始值。这些对象在程序结束前都可以使用,且在需要向实际的标准流打印内容时很有用,无论 sys.std* 对象是否已重定向。如果实际文件已经被覆盖成一个损坏的对象了,那它也可用于将实际文件还原成能正常工作的文件对象
    • 在Python中可以重新分配stdout这些变量,将代码输出重定向到stdout以外的文件对象,类似于shell的重定向概念
      sys.stdout = open(file='file.txt', mode='a')  # 重定向stdout到文件对象
      print('sys')  # 输出到文件对象
      sys.stdout.close()
      sys.stdout = sys.__stdout__  # stdout重定向到将初始值
      

sys.stdin

  • 是什么:stdin是解释器用于标准输入的文件对象
    import sys
    s = sys.stdin
    print(s, type(s))
    
    <_io.TextIOWrapper name='' mode='r' encoding='utf-8'> 
    
  • 用途:用于所有交互式输入(包括对 input() 的调用)。input()功能即按照规则从标准输入中读取字符串
    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
    
  • sys.stdin.readline()与input()的关系:sys.stdin.readline()与input()都是以换行为结束输入的标志。区别为前者会将标准输入全部读取包括尾部换行符’\n‘,后者会将尾部换行符删除
    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  # 字符串长度不同
    
  • 标准输入读取的三种方式:sys.stdin.read、sys.stdin.readline、sys.stdin.readlines
    • read可一次性读取多行内容,不读出换行符,换行符直接在字符串内起作用,mac中以command+D结束读取。注意,read为读取多行内容,所以最后输入为换行符,否则不会读取最后的内容
      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
      
    • readline只能读取单行内容,以换行符结束读取。不读出换行符,换行符直接在字符串内起作用
    • readlines会读取多行内容,返回结果为列表,元素为每行内容包括换行符,会读出换行符,mac中以command+D结束读取。注意readlines为读取多行内容,最后输入需为换行符
      import sys
      a = sys.stdin.readlines()
      print(a)
      
      ab  # 标准输入
      cd^D  # 标准输入。没有换行,直接以command+D结束
      ['ab\n']  # 列表,每行内容为元素,读出换行符
      
      ab  # 标准输入
      cd  # 标准输入。存在换行
      ^D  # 以command+D结束
      ['ab\n', 'cd\n']  # 列表,元素为每行内容(包括换行符),换行符会读出
      

sys.stderr

  • 是什么:stderr是解释器用于标准错误的文件对象
    import sys
    stderr = sys.stderr
    print(stderr, type(stderr))
    
    <_io.TextIOWrapper name='' mode='w' encoding='utf-8'> 
    
  • 用途:解释器自身的提示符和它的错误消息都发往 stderr
    import sys
    sys.stderr = open(file='file.txt', mode='w')  # 将stderr重定向到另一个文件对象
    a = 3 / 0  # 使程序崩溃并将调试信息输出到stderr管道中
    sys.stderr.close()  # 关闭该文件对象
    sys.stderr = sys.__stderr__  # 将标准错文件对象重定向到初始标准错误文件对象
    

sys模块常用介绍 - 系统相关的参数和函数_第1张图片

sys.last_type & sys.last_value & sys.last_traceback

  • 是什么:这三个变量并非总是有定义,仅当有异常未处理,且解释器打印了错误消息和堆栈回溯时,才会给它们赋值。变量含义与sys.exc_info() 返回值含义相同,type 是正在处理的异常类型(它是 BaseException 的子类);value 是异常实例(异常类型的实例);traceback 是一个 回溯对象,该对象封装了最初发生异常时的调用堆栈
  • 预期用途:允许交互中的用户导入调试器模块,进行事后调试,而不必重新运行导致错误的命令,通常使用 import pdb; pdb.pm() 进入程序发生异常导致崩溃后的事后调试器,跟踪异常程序最后的堆在信息
    • 演示的脚本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$ 
      

函数

sys._current_frames()

  • 是什么:该函数返回一个字典,存放着每个线程的标识符与(调用本函数时)该线程栈顶的帧(当前活动的帧)之间的映射
  • 用途举例:用于调试死锁,因为该函数不需要死锁线程的配合,只要这些线程的调用栈保持死锁,它们就是冻结的。当在调用本代码来检查栈顶的帧的那一刻,非死锁线程返回的帧可能与该线程当前活动的帧没有任何关系
    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
    

sys._getframe([depth])

  • 是什么:该函数返回来自调用栈的一个帧对象,depth 的默认值是 0,返回调用栈顶部的帧
  • 传入参数:如果传入可选整数 depth,则返回从栈顶往下相应调用层数的帧对象。如果该数比调用栈更深,则抛出 ValueError
  • 注意: 这个函数应该只在内部为了一些特定的目的使用。不保证它在所有 Python 实现中都存在
  • 用途举例:可用于定位追踪问题,确定程序的调用链,打印程序执行过程中的每一个函数调用,包括函数名称、所在文件等
    • 通过sys._getframe()获取当前调用栈的所有帧对象,可以得到详细的调用信息。但只能做到从调用链最深处获得调用信息,即获取sys._getframe()执行前的函数调用信息,不能得到之后的函数调用信息
      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
      
    • 通过sys.setprofile(profilefunc)设置性能分析函数,即可在调用某函数和从某函数返回时,执行性能分析函数,得到函数的全部调用链及信息

sys.breakpointhook()

  • 是什么:本钩子函数由内建函数 breakpoint() 调用,默认情况下,它将进入 pdb 调试器,但可以将其改为任何其他函数,以选择使用哪个调试器
  • 默认情况下调用breakpoint() :sys.breakpointhook() 调用 pdb.set_trace() 且没有参数
    • 查询环境变量PYTHONBREAKPOINT,若为"0"则breakpointhook() 立即返回,表示在断点无操作
      import os
      
      os.environ['PYTHONBREAKPOINT'] = '0'
      print('run')
      breakpoint()  # 该断点处无操作
      print('finish')
      
    • 若PYTHONBREAKPOINT为未设置或为空字符串,则调用pdb.set_trace()
      import os
      
      os.environ['PYTHONBREAKPOINT'] = ''
      print('run')
      breakpoint()  # 进入pdb.set_trace()调试器
      print('finish')
      
  • PYTHONBREAKPOINT指定运行函数:指定函数以package.subpackage.module.function方式导入,当breakpoint()运行时将*args 和 **kws 传入breakpointhook(),且无论function()返回什么,sys.breakpointhook() 都将返回到內建函数 breakpoint()
    • 如果在导入 PYTHONBREAKPOINT 指定的可调用对象时出错,则将报告一个 RuntimeWarning 并忽略断点
    • 如果以编程方式覆盖sys.breakpointhook(),则不会查询PYTHONBREAKPOINT
      # 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
      

sys.displayhook(value)

  • 是什么:使用repr函数处理value,将处理结果写入标准输出,并保存到builtins._ 。以下为实现函数功能的伪代码:
    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
    
  • 用途:在交互式会话中运行表达式产生结果后,将在结果上调用该函数。若要自定义处理结果,也可将将display指向一个单参数函数
    >>> import sys
    >>> def displayhook2(value):
        	    text = '规范字符串:' + repr(value)
        	    sys.stdout.write(text)
        	    sys.stdout.write("\n")
    ... 
    >>> sys.displayhook = displayhook2
    >>> 2 + 3
    规范字符串:5
    

sys.excepthook(type, value, traceback)

  • 是什么:一个处理任何未捕获异常(除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
    

sys.exc_info()

  • 是什么:该函数返回当前正在处理的异常的信息。返回的信息仅限于当前线程和当前堆栈帧;若当前堆栈帧没有正在处理的异常,则从下级被调用的堆栈帧或上级调用者等位置获取,直到找到正在处理异常(即执行except子句)的堆栈帧;若整个堆栈都没有正在处理的异常,则返回包含三个 None 值的元组。用法为在except子句中调用该函数,可返回当前正在处理的异常信息
    • 堆栈:是一块连续的内存,堆栈中的内存单元在函数生命周期内可以使用,当函数返回时内存单元会被释放(释放后其它函数就可以用)
    • 堆栈帧:在堆栈中对当前正在运行的函数分配的区域,包括传入的参数、返回地址(函数结束后必须跳转到该地址,即主调函数的断点处)、函数所用的内部存储单元(函数存储在堆栈上的局部变量),详参:什么是堆栈帧
  • 返回值:type 是正在处理的异常类型(它是 BaseException 的子类),value 是异常实例(异常类型的实例),traceback 是一个 回溯对象,该对象封装了最初发生异常时的调用堆栈
  • 几种获取异常信息的方式
    • try…except…语句,捕获异常类型与描述
      import sys
      
      f = lambda x: x / 0
      try:
          f(5)
      except Exception as e:
          print(e, type(e))
      
      division by zero 
      
    • 在except子句中,sys.exc_info()获取异常信息,处理异常回溯对象
      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
      
    • 在except子句中,sys.exc_info()获取异常信息,traceback模块处理异常回溯对象
      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
      
    • 通过traceback模块获取异常信息
      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
      

sys.exit([arg])

  • 是什么:抛出一个SystemExit异常,发出退出解释器的信号。可用于退出程序、暂时终止程序等情况
  • 传入参数:可选参数 arg 可以是表示退出状态的整数(默认为 0),也可以是其他类型对象
    • 传入参数是整数,则 shell 等将 0 视为“成功终止”,非零值(1-127)视为“异常终止”。Unix 程序通常用 2 表示命令行语法错误,用 1 表示所有其他类型的错误。
    • 传入参数是其他类型对象,如果传入 None 等同于传入 0,如果传入其他对象则将其打印至 stderr,且退出代码为 1。注意,sys.exit(“some error message”) 可以在发生错误时快速退出程序
      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')
      
  • SystemExit异常:由 sys.exit() 函数引发,继承自 BaseException 而不是 Exception以确保不会被处理 Exception 的代码意外捕获,这允许此异常正确地向上传播并导致解释器退出。对 sys.exit() 的调用会被转换为一个异常以便能执行清理处理程序 (try 语句的 finally 子句)
    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
    
    • SystemExit异常未被处理,Python 解释器就将退出,不会打印任何栈回溯信息
    • 捕获SystemExit异常,执行一些清理操作,并继续执行后面程序
      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
      
  • 3.6版本更改:在 Python 解释器捕获 SystemExit 后,如果在清理中发生错误(如清除标准流中的缓冲数据时出错),则退出状态码将变为 120

sys.getallocatedblocks()

  • 是什么:返回解释器当前已分配的内存块数,无论它们大小如何。如果当前 Python 构建或实现无法合理地计算此信息,允许 getallocatedblocks() 返回 0
  • 用途:本函数主要用于跟踪和调试内存泄漏。因为解释器有内部缓存,所以不同调用之间结果会变化。可能需要调用 _clear_type_cache() 和 gc.collect() 使结果更容易预测

sys.getrefcount(object)

  • 是什么:返回 object 的引用计数。返回的计数通常比预期的多一,因为它包括了作为 getrefcount() 参数的这一次(临时)引用。
  • 引用计数:记录该对象当前被引用的次数,每当新的引用指向该对象时,它的引用计数加1,每当该对象的引用失效时计数减1,一旦对象的引用计数为0,该对象立即被回收。引用计数加1存在以下情况
    • 对象被创建。注意,Python启动解释器时会创建一个小整数池,在-5~256之间的整数对象会被自动加载到内存中等待调用,因此该范围的整数对象引用计数会加1
    • 对象被引用
    • 对象作为参数传递到函数中
    • 对象作为元素存储到容器中

sys.getsizeof(object[, default])

  • 是什么:用于返回对象的大小(以字节为单位)的函数。该函数只计算直接分配给对象的内存消耗,不计算它所引用的对象的内存消耗。当对象不提供计算大小的方法时,如果传入过 default 则返回它,否则抛出 TypeError 异常
  • 传入参数:object,该对象可以是任何类型
    • 所有内建对象返回的结果都是正确的,但对于第三方扩展不一定正确,因为这与具体实现有关
    • 如果对象由垃圾回收器管理,则 getsizeof() 将调用对象的 __sizeof__ 方法,并在上层添加额外的垃圾回收器
  • 如何计算容器及其所有物的大小:参考 COMPUTE MEMORY FOOTPRINT OF AN OBJECT AND ITS CONTENTS
    • 处理方法:递归调用getsizeof()来计算容器及其所有物的大小。通过处理器将容器元素、对象属性转为生成器,使用map函数调用getsizeof()来处理生成器生成的每个元素
    • 自定义对象计算方式:计算对象及其属性值的大小。当__slots__在类中定义时,__dict__在实例中不会存在,根据类的__slots__定义的所有属性来获取实例的所有属性。当__slots__不存在时,通常存在__dict__,根据实例的__dict__来获取实例的所有属性。当对象没有__dict__、__dict__意味着没有属性,sys.getsizeof()返回实际的正确值
    • __slot__简介:允许显示声明数据成员,并拒绝创建__dict__和__weakref__(除非在__slots__中明确声明或在父节点中可用),可理解为阻止类动态创建属性。
      • 当实例化对象达到数百万个,使用__slots__能大幅节省内存,显著提高属性查找速度。因为Python在各个实例中__dict__ 的字典里存储实例属性,为了使用底层的散列表提升访问速度,字典会消耗大量内存,但__slots__会使其在元组中存储实例属性
      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))
      

sys.intern(string)

  • 是什么:该函数将 string 插入 “interned” (驻留)字符串表,返回被插入的字符串(string 本身或副本)。驻留字符串对提高字典查找的性能很有用,如果字典中的键已驻留,且所查找的键也已驻留,则键(after hashing)的比较可以用指针代替字符串来比较(比较内存地址而非内容)。Python 程序使用到的名称会被自动驻留,且用于保存模块、类或实例属性的字典的键也已驻留,参考python sys.intern是做什么的
  • 注意:驻留字符串不是永久存在的,对 intern() 返回值的引用必须保留下来,才能发挥驻留字符串的优势
    >>> 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
    >>> 
    

sys.setprofile(profilefunc) & sys.getprofile()

  • 是什么
    • setprofile(profilefunc)是一个函数,用于设置系统的性能分析函数
    • getprofile()是一个函数,返回由 setprofile(profilefunc) 设置的性能分析函数
  • 性能分析函数
    • 是什么:在 Py​​thon 中能够实现一个 Python 源代码性能分析器。性能分析函数是特定于单个线程的,由于性能分析器无法得知线程之间的上下文切换,因此在存在多个线程的情况下使用它是没有意义的。因为函数的返回值不会被用到,所以可以简单地返回 None。性能分析函数中的错误将导致其自身被解除设置。
    • 调用方式:类似于系统的跟踪函数(参阅 settrace() ),但性能分析函数是通过不同的事件调用的,仅在调用某函数和从某函数返回时才会调用性能分析函数,但即使某函数发生异常也会算作返回事件
    • 传入参数:frame为当前的堆栈帧。event 是一个字符串,包括’call’、‘return’、‘c_call’、‘c_return’ 或 ‘c_exception’,arg 取决于event。以下为event事件含义
      • call 表示调用了某个函数(或进入了其他的代码块)。性能分析函数将被调用,arg 为 None
      • return 表示某个函数(或别的代码块)即将返回。性能分析函数将被调用,arg 是即将返回的值,如果此次返回事件是由于抛出异常,arg 为 None
      • c_call 表示即将调用某个 C 函数,它可能是扩展函数或是内建函数。arg 是 C 函数对象
      • c_return 表示返回了某个 C 函数。arg 是 C 函数对象
      • c_exception 表示某个 C 函数抛出了异常。arg 是 C 函数对象
    • 用途举例:由于调用某函数和从某函数返回时才会调用性能分析函数,因此可以获取程序中的函数调用过程,例如获取某段源码中的函数调用过程,参考神奇的python追踪术—sys.setprofile
      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
      

sys.setrecursionlimit(limit) & sys.getrecursionlimit()

  • 是什么
    • setrecursionlimit(limit)将 Python 解释器堆栈的最大深度设置为 limit
    • getrecursionlimit()返回当前的递归限制值,即 Python 解释器堆栈的最大深度。该限制可以防止无限递归导致的 C 堆栈溢出和 Python 崩溃
  • 注意事项:不同平台所允许的最高限值不同。当用户有需要深度递归的程序且平台支持更高的限值,可能就需要调高限值。进行该操作需要谨慎,因为过高的限值可能会导致崩溃。如果新的限值低于当前的递归深度,将抛出 RecursionError 异常
    >>> import sys
    >>> sys.getrecursionlimit()
    1000
    

sys.settrace(tracefunc) & sys.gettrace()

  • 是什么

    • sys.settrace(tracefunc)用于设置系统的跟踪函数,使得用户在 Python 中就可以实现 Python 源代码调试器。该函数是特定于单个线程的,若要让调试器支持多线程,可使用 threading.settrace()
    • sys.gettrace()会返回由 settrace() 设置的跟踪函数。该函数仅用于实现调试器,性能分析器,打包工具等。它的行为是实现平台的一部分,而不是语言定义的一部分,因此并非在所有 Python 实现中都可用
  • 传入参数: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 属性

你可能感兴趣的:(python基础,python)