堆栈跟踪中的Python函数的名称

时间:2016-11-17 17:50:18

标签: python metaprogramming

在Python2和Python3中,在堆栈跟踪中未使用函数的__name__,而是使用原始名称(在def之后指定的名称)。

考虑一下这个例子:

import traceback

def a():
    return b()

def b():
    return c()

def c():
    print("\n".join(line.strip() for line in traceback.format_stack()))

a.__name__ = 'A'
b.__name__ = 'B'
c.__name__ = 'C'

a();

输出结果为:

File "test.py", line 16, in <module>
    a();
File "test.py", line 4, in a
    return b()
File "test.py", line 7, in b
    return c()
File "test.py", line 10, in c
    print("\n".join(line.strip() for line in traceback.format_stack()))

为什么这样?如何更改堆栈跟踪中使用的名称?那么使用__name__属性在哪里?

2 个答案:

答案 0 :(得分:9)

所以,基本上每个函数都有三个可以被认为是函数 name 的东西:

代码块的原始名称

它存储在f.__code__.co_name中(其中f是函数对象)。如果您使用def orig_name创建函数,orig_name就是该名称。对于lambas来说,它是<lambda>

此属性是只读的,无法更改。因此,我知道在运行时使用自定义名称创建函数的唯一方法是exec

exec("""def {name}():
  print '{name}'
""".format(name='any')) in globals()

any()  # prints 'any'

(问题评论中提到了更多低级way to do this。)

co_name的不变性实际上是有道理的:你可以确定你在调试器中看到的名称(或者只是堆栈跟踪)与你在源代码中看到的完全相同(以及文件名)和行号)。

函数对象的__name__属性

它也是func_name的别名。

您可以对其进行修改(orig_name.__name__ = 'updated name')并确实每天都会进行修改:@functools.wraps将已修饰函数的__name__复制到新版本。

__name__这样的工具使用

pydoc,这就是您需要@functools.wraps的原因:所以您没有看到文档中每个装饰器的技术细节。看一下这个例子:

from functools import wraps

def decorator1(f):
    def decorated(*args, **kwargs):
        print 'start1'
        f(*args, **kwargs)
    return decorated

def decorator2(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        print 'start2'
        f(*args, **kwargs)
    return decorated

@decorator1
def test1():
    print 'test1'

@decorator2
def test2():
    print 'test2'

以下是pydoc输出:

FUNCTIONS
    decorator1(f)

    decorator2(f)

    test1 = decorated(*args, **kwargs)

    test2(*args, **kwargs)

使用wraps时,文档中没有decorated的迹象。

参考名称

可以称为函数名称的另一件事(虽然它几乎不是)是存储对该函数的引用的变量或属性的名称。

如果使用def name创建函数,name属性将添加到当前范围。如果是lambda,您应该将结果分配给某个变量:name = lambda: None

显然,您可以为同一个函数创建多个引用,并且所有引用都可以有不同的名称。

这三件事彼此连接的唯一方法是def foo语句创建函数对象,__name____code__.co_name等于foo并分配它到当前范围的foo属性。但它们不受任何方式的束缚,可能彼此不同:

import traceback                             

def make_function():                         
    def orig_name():                         
        """Docstring here                    
        """                                  
        traceback.print_stack()              
    return orig_name                         

globals()['name_in_module'] = make_function()
name_in_module.__name__ = 'updated name'     

name_in_module()                             

输出:

  File "my.py", line 13, in <module>
    name_in_module()
  File "my.py", line 7, in orig_name
    traceback.print_stack()

是pydoc:

FUNCTIONS
    make_function()

    name_in_module = updated name()
        Docstring here

我感谢其他人的意见和答案,他们帮助我组织了我的想法和知识

答案 1 :(得分:3)

试图探索enter image description here实施,绝对不是专家。正如评论中所指出的,当打印f的堆栈条目时,属性f.__code__.co_name CPython。此外,f.__name__ is usedf.__code__.co_nameis initially set前者,后者未相应修改。

因此,我试图直接修改它,但不可能:

>>> f.__code__.co_name = 'g'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: readonly attribute
>>>

为什么有两种方式来说出一个功能的名字?好吧,but when you modify__name__是为&#34;类,函数,方法,描述符或生成器实例&#34;定义的,因此在函数的情况下,它映射到该属性,对于其他对象它将映射到其他东西。