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.
Consider the example:
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();
The output is:
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()))
Why so? How do I change the name that is used in the stack trace? Where is the __name__
attribute used then?
So, basically every function has three things that can be considered being name of the function:
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>
.
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.)
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).
__name__
attribute of the function objectIt's also aliased to func_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__
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'
Here is the pydoc
output:
FUNCTIONS
decorator1(f)
decorator2(f)
test1 = decorated(*args, **kwargs)
test2(*args, **kwargs)
With wraps
there is no sign of decorated
in the documentation.
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.
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.
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()
Output:
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
I thank other people for comments and answers, they helped me to organize my thoughts and knowledge.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With