堆栈跟踪中Python函数的名称

堆栈跟踪中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__ =

在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__ = '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
,而不是函数对象的名称。@hivert
func\u name
确实是一个
\uu name\uuuuu
别名,有明确的文档记录:@DanD。我甚至可以用动态名称创建新函数,但我不知道如何做(保存
eval
),那么
\uu name\uu
呢?它是如何使用的?“它从来没有被CPython修改过”-不,它完全是。你只是在寻找错误的东西。见鬼,如果您只查找对
long\u add
的直接引用,那么CPython似乎从不添加整数。不经意间,
functools.wrapps
设置函数的
\uuuu name\uuuuu
,并且
functools.wrapps在CPython源代码中的许多地方都被使用。@user2357112有趣,我完全没有想到这一点。从答案中删除