堆栈跟踪中的Python函数名称 [英] Name of a Python function in a stack trace

查看:66
本文介绍了堆栈跟踪中的Python函数名称的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In both Python2 and Python3, in the stack trace the __name__ of a function is not used, the original name (the one that is specified after def) is used instead.

考虑示例:

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__属性在哪里使用?

Why so? How do I change the name that is used in the stack trace? Where is the __name__ attribute used then?

推荐答案

因此,基本上每个函数都具有三个可以视为函数的 name 的东西:

So, basically every function has three things that can be considered being name of the function:

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

It's stored in the f.__code__.co_name (where f is the function object). If you use def orig_name to create function, orig_name is that name. For lambas it's <lambda>.

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

This attribute is readonly and can't be changed. So the only way to create function with the custom name in runtime I'm aware of is exec:

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

any()  # prints 'any'

(还有更多底层方式在对该问题的评论中提到的.)

(There is also more low-level way to do this that was mentioned in a comment to the question.)

co_name的不变性实际上是有道理的:通过这一点,您可以确保在调试器中看到的名称(或只是堆栈跟踪)与在源代码中看到的名称完全相同(以及文件名和行)数字).

The immutability of co_name actually makes sense: with that you can be sure that the name you see in the debugger (or just stack trace) is exactly the same you see in the source code (along with the filename and line number).

它也别名为func_name.

您可以对其进行修改(orig_name.__name__ = 'updated name'),并且您每天都可以进行修改: @functools.wraps 将修饰后的函数的__name__复制到新函数中.

You can modify it (orig_name.__name__ = 'updated name') and you surely do on a daily basis: @functools.wraps copies the __name__ of the decorated function to the new one.

__name__pydoc之类的工具使用,这就是为什么需要@functools.wraps的原因:因此,您不会在文档中看到每个装饰器的技术细节.看例子:

__name__ is used by tools like pydoc, that's why you need @functools.wraps: so you don't see the technical details of every decorator in your documentation. Look at the example:

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的标志.

With wraps there is no sign of decorated in the documentation.

还有一个可以叫做 function name 的东西(虽然几乎不是)是变量或属性的名称,该变量或属性存储了对该函数的引用.

One more thing that can be called function name (though it hardly is) is the name of a variable or an attribute where reference to that function is stored.

如果使用def name创建函数,则会将name属性添加到当前作用域.如果是lambda,则应将结果分配给某个变量:name = lambda: None.

If you create function with def name, the name attribute will be added to the current scope. In case of lambda you should assign the result to some variable: name = lambda: None.

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

Obviously you can create more than one reference to the same function and all that references can have different names.

所有三件事相互联系的唯一方法是def foo语句,该语句创建同时__name____code__.co_name均等于foo的函数对象并将其分配给对象的foo属性.当前范围.但是它们没有任何约束,并且可以彼此不同:

The only way all that three things are connected to each other is the def foo statement that creates function object with both __name__ and __code__.co_name equal to foo and assign it to the foo attribute of the current scope. But they are not bound in any way and can be different from each other:

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


我感谢其他人的评论和回答,他们帮助我整理了思想和知识.

这篇关于堆栈跟踪中的Python函数名称的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆