Nombre de una función de Python en un seguimiento de stack

Tanto en Python2 como en Python3, en el seguimiento de stack no se usa el __name__ de una función, sino que se usa el nombre original (el que se especifica después de def ).

Considera el ejemplo:

 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(); 

La salida es:

 File "test.py", line 16, in  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())) 

¿Porque? ¿Cómo cambio el nombre que se usa en el seguimiento de stack? ¿Dónde se usa el atributo __name__ entonces?

    Entonces, básicamente, cada función tiene tres cosas que pueden considerarse como el nombre de la función:

    El nombre original del bloque de código.

    Se almacena en el f.__code__.co_name (donde f es el objeto de función). Si usa def orig_name para crear la función, orig_name es ese nombre. Para las lambas es .

    Este atributo es de solo lectura y no se puede cambiar. Entonces, la única forma de crear una función con el nombre personalizado en tiempo de ejecución que conozco es exec :

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

    (También hay una forma más de bajo nivel para hacer esto que se mencionó en un comentario a la pregunta).

    La inmutabilidad de co_name realmente tiene sentido: con eso puede estar seguro de que el nombre que ve en el depurador (o simplemente el seguimiento de la stack) es exactamente el mismo que se ve en el código fuente (junto con el nombre del archivo y el número de línea).

    El atributo __name__ del objeto de función

    También tiene un alias para func_name .

    Puede modificarlo ( orig_name.__name__ = 'updated name' ) y seguramente lo hace a diario: @functools.wraps copia el __name__ de la función decorada a la nueva.

    __name__ es usado por herramientas como pydoc , por eso necesita @functools.wraps : para que no vea los detalles técnicos de cada decorador en su documentación. Mira el ejemplo:

     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' 

    Aquí está la salida pydoc :

     FUNCTIONS decorator1(f) decorator2(f) test1 = decorated(*args, **kwargs) test2(*args, **kwargs) 

    Con las wraps no hay signos de decorated en la documentación.

    Nombre de la referencia

    Una cosa más que se puede llamar nombre de la función (aunque casi no lo es) es el nombre de una variable o un atributo donde se almacena la referencia a esa función.

    Si crea una función con el def name , el atributo de name se agregará al ámbito actual. En el caso de lambda , debe asignar el resultado a alguna variable: name = lambda: None .

    Obviamente, puede crear más de una referencia a la misma función y todas esas referencias pueden tener nombres diferentes.


    La única forma en que las tres cosas están conectadas entre sí es la statement def foo que crea el objeto de función con __name__ y __code__.co_name igual a foo y lo asigna al atributo foo del ámbito actual. Pero no están vinculados de ninguna manera y pueden ser diferentes entre sí:

     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() 

    Salida:

      File "my.py", line 13, in  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 

    Agradezco a otras personas por sus comentarios y respuestas, me ayudaron a organizar mis pensamientos y conocimientos .

    Intenté explorar la implementación de CPython , definitivamente no soy un experto. Como se señaló en los comentarios, cuando se imprime la entrada de stack de f, se utiliza el atributo f.__code__.co_name . Además, f.__name__ se establece inicialmente en f.__code__.co_name , pero cuando modifica el primero, este último no se modifica en consecuencia.

    Por lo tanto, traté de modificar eso directamente, pero no es posible:

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

    ¿Por qué hay dos maneras de decir el nombre de una función? Bueno, de acuerdo con la documentación , __name__ se define para “clase, función, método, descriptor o instancia de generador”, por lo que en el caso de funciones se asigna a ese atributo, para otros objetos se asignará a otra cosa.