堆栈跟踪中 Python 函数的名称
Name of a Python function in a stack trace
在Python2和Python3中,在stack trace中没有使用函数的__name__
,原名(在[=14=之后指定的那个) ]) 被改用。
考虑示例:
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__
属性用在哪里呢?
前者试探CPython implementation, definitely not an expert. As pointed out in the comments, when the stack entry of f is printed, the attribute f.__code__.co_name
is used. Also, f.__name__
is initially set to f.__code__.co_name
, but when you modify,后者未做相应修改
因此,我试图直接修改它,但这是不可能的:
>>> f.__code__.co_name = 'g'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: readonly attribute
>>>
为什么函数名有两种说法?好吧,according to the documentation、__name__
是为 "class, function, method, descriptor, or generator instance" 定义的,所以在函数的情况下它映射到该属性,对于其他对象它将映射到其他东西。
所以,基本上每个函数都有三个可以被认为是函数的名称的东西:
代码块原名
它存储在 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
我感谢其他人的评论和回答,他们帮助我整理了我的思想和知识。
在Python2和Python3中,在stack trace中没有使用函数的__name__
,原名(在[=14=之后指定的那个) ]) 被改用。
考虑示例:
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__
属性用在哪里呢?
前者试探CPython implementation, definitely not an expert. As pointed out in the comments, when the stack entry of f is printed, the attribute f.__code__.co_name
is used. Also, f.__name__
is initially set to f.__code__.co_name
, but when you modify,后者未做相应修改
因此,我试图直接修改它,但这是不可能的:
>>> f.__code__.co_name = 'g'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: readonly attribute
>>>
为什么函数名有两种说法?好吧,according to the documentation、__name__
是为 "class, function, method, descriptor, or generator instance" 定义的,所以在函数的情况下它映射到该属性,对于其他对象它将映射到其他东西。
所以,基本上每个函数都有三个可以被认为是函数的名称的东西:
代码块原名
它存储在 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
我感谢其他人的评论和回答,他们帮助我整理了我的思想和知识。