堆栈跟踪中Python函数的名称
在Python2和Python3中,在堆栈跟踪中不使用函数的堆栈跟踪中Python函数的名称,python,metaprogramming,Python,Metaprogramming,在Python2和Python3中,在堆栈跟踪中不使用函数的\uuu name\uu,而是使用原始名称(在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__ =
\uuu name\uu
,而是使用原始名称(在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();
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'
输出为:
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()))
文件“test.py”,第16行,在
a();
文件“test.py”,第4行,在
返回b()
文件“test.py”,b中第7行
返回c()
文件“test.py”,第10行,c语言
打印(“\n”.join(traceback.format\u stack()中的行的line.strip()))
为什么会这样?如何更改堆栈跟踪中使用的名称?当时使用的\uuu name\uuuu
属性在哪里?试图探索实现,但肯定不是专家。正如注释中所指出的,当打印f的堆栈条目时,属性f.\uuuu code\uuuu.co\u name
。另外,f.\uuuuu name
至f.\uuuu code\uuuu.co\u name
,前者不作相应修改
因此,我试图直接修改,但不可能:
>>> f.__code__.co_name = 'g'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: readonly attribute
>>>
>>f.uuuu code.co\u name='g'
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
TypeError:只读属性
>>>
为什么说函数名有两种方法?好的,
\uuuuuuu name\uuuuuuuuuuuu>是为“类、函数、方法、描述符或生成器实例”定义的,因此在函数映射到该属性的情况下,对于其他对象,它将映射到其他对象。因此,基本上每个函数都有三个可以被视为函数名称的东西:
代码块的原始名称
它存储在f.uuu code\uuuu.co\u name
中(其中f
是函数对象)。如果使用def orig_name
创建函数,orig_name
就是该名称。对于lambas,它是
此属性为只读,无法更改。因此,我知道在运行时使用自定义名称创建函数的唯一方法是exec
:
exec("""def {name}():
print '{name}'
""".format(name='any')) in globals()
any() # prints 'any'
(在对该问题的评论中,还提到了更低级的问题。)
co_name
的不变性实际上是有道理的:通过它,您可以确保在调试器中看到的名称(或堆栈跟踪)与在源代码中看到的名称完全相同(以及文件名和行号)
函数对象的\uuuu name\uuuu
属性
它还别名为func\u name
您可以修改它(orig\u name.\uu\u name=“updated name”
),而且您每天都可以这样做:将修饰函数的\uu name\uuu
复制到新函数
\uuuu name\uuuu
是由pydoc
等工具使用的,这就是为什么您需要@functools.wrapps
:这样您就不会在文档中看到每个装饰器的技术细节。看看这个例子:
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();
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)
使用包装
时,文档中没有装饰的迹象
引用的名称
还有一件事可以称为函数名(尽管它很难称为函数名),那就是存储对该函数的引用的变量或属性的名称
如果使用def name
创建函数,则name
属性将添加到当前范围。在lambda
的情况下,应将结果分配给某个变量:name=lambda:None
显然,您可以为同一个函数创建多个引用,所有引用都可以有不同的名称
所有这三个东西相互连接的唯一方式是def foo
语句,该语句创建同时具有\uuuu name\uuu
和\uu code\uu.co\u 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()
我感谢其他人的评论和回答,他们帮助我整理了我的思想和知识。a.\uu name\uuuu似乎是a.func\u name的别名。它们似乎都没有被堆栈跟踪使用。函数的代码对象也存储名称,但该属性是只读的。我认为这就是回溯中使用的属性。要更改该名称,您需要从代码对象的属性重新创建代码对象,并将其分配回函数对象的代码属性。请看,这很有趣-它似乎使用了代码对象的名称a.\uuuu code\uuuu.co\u name
,而不是函数对象的名称。@hivertfunc\u name
确实是一个\uu name\uuuuu
别名,有明确的文档记录:@DanD。我甚至可以用动态名称创建新函数,但我不知道如何做(保存eval
),那么\uu name\uu
呢?它是如何使用的?“它从来没有被CPython修改过”-不,它完全是。你只是在寻找错误的东西。见鬼,如果您只查找对long\u add
的直接引用,那么CPython似乎从不添加整数。不经意间,functools.wrapps
设置函数的\uuuu name\uuuuu
,并且functools.wrapps在CPython源代码中的许多地方都被使用。@user2357112有趣,我完全没有想到这一点。从答案中删除