720

In Python, without using the traceback module, is there a way to determine a function's name from within that function?

Say I have a module foo with a function bar. When executing foo.bar(), is there a way for bar to know bar's name? Or better yet, foo.bar's name?

#foo.py  
def bar():
    print "my name is", __myname__ # <== how do I calculate this at runtime?
Georgy
  • 12,464
  • 7
  • 65
  • 73
Rob
  • 7,420
  • 3
  • 17
  • 12

27 Answers27

564
import inspect

def foo():
   print(inspect.stack()[0][3])
   print(inspect.stack()[1][3])  # will give the caller of foos name, if something called foo

foo()

output:

foo
<module_caller_of_foo>
Tms91
  • 3,456
  • 6
  • 40
  • 74
  • 103
    You could also use: ```print(inspect.currentframe().f_code.co_name)``` or to get the caller's name: ```print(inspect.currentframe().f_back.f_code.co_name)```. I think it should be faster since you don't retrieve a list of all the stack frames as ```inspect.stack()``` does. – Michael Feb 23 '14 at 10:25
  • 14
    `inspect.currentframe().f_back.f_code.co_name` doesn't work with a decorated method whereas `inspect.stack()[0][3]` does... – Dustin Wyatt Feb 15 '16 at 18:08
  • 8
    Please note: inspect.stack() can incur heavy performance overhead so use sparingly! On my arm box it took 240ms to complete (for some reason) – gardarh Dec 08 '16 at 12:59
  • Seems to me like something present in the Python recursion machinery might be employed to do this more efficiently – Tom Russell Jul 26 '17 at 08:18
  • 1
    This gets the **code object's** name, not the function's name. Function names and code object names often diverge when using decorators, due to function `__name__` reassignment. The code object's name will not reflect function `__name__` changes. – user2357112 Mar 25 '20 at 01:17
  • 3
    @Michael please post your comment as an answer . – Давид Шико May 17 '20 at 19:15
  • 3
    What is the magic number `3` in the code? – tommy.carstensen Oct 18 '22 at 11:13
279

Python doesn't have a feature to access the function or its name within the function itself. It has been proposed but rejected. If you don't want to play with the stack yourself, you should either use "bar" or bar.__name__ depending on context.

The given rejection notice is:

This PEP is rejected. It is not clear how it should be implemented or what the precise semantics should be in edge cases, and there aren't enough important use cases given. response has been lukewarm at best.

David Foerster
  • 1,461
  • 1
  • 14
  • 23
Rosh Oxymoron
  • 20,355
  • 6
  • 41
  • 43
  • 30
    `inspect.currentframe()` is one such way. – Yuval Sep 20 '14 at 10:47
  • 70
    Combining @CamHart's approach with @Yuval's avoids "hidden" and potentially deprecated methods in @RoshOxymoron's answer as well as numerical indexing into the stack for @neuro/@AndreasJung's answer: `print(inspect.currentframe().f_code.co_name)` – hobs Mar 17 '15 at 21:43
  • 3
    is it possible to summarize why its been rejected? – Charlie Parker Jun 15 '17 at 18:27
  • 10
    why is this the chosen answer? Question isn't about accessing the current function or the module itself, just the name. And the stacktrace/debugging features already have this information. – nurettin Apr 03 '19 at 11:23
  • As of today, tested within my CPython 3.7.2 bar.__name__ does work. For the simplest unaccepted answer for Python 3.x + see [Vagiz Duseev](https://stackoverflow.com/users/2472360/vagiz-duseev)'s answer below [Answer](https://stackoverflow.com/a/64110183/502519). – DevPlayer May 23 '21 at 13:29
  • @hobs `Combining @CamHart's approach with @Yuval's avoids "hidden" and potentially deprecated methods in @RoshOxymoron's answer as well as numerical indexing into the stack for @neuro/@AndreasJung's answer` - hmm … However did you `@notify` so much guys at once ? – TheEagle Jun 22 '21 at 22:29
  • 6
    This answer is too old. It is now wrong. Please delete it. – Florian Jun 23 '21 at 12:21
271

There are few ways to get the same result:

import sys
import inspect

def what_is_my_name():
    print(inspect.stack()[0][0].f_code.co_name)
    print(inspect.stack()[0][3])
    print(inspect.currentframe().f_code.co_name)
    print(sys._getframe().f_code.co_name)

Note that the inspect.stack calls are thousands of times slower than the alternatives:

$ python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][0].f_code.co_name'
1000 loops, best of 3: 499 usec per loop
$ python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][3]'
1000 loops, best of 3: 497 usec per loop
$ python -m timeit -s 'import inspect, sys' 'inspect.currentframe().f_code.co_name'
10000000 loops, best of 3: 0.1 usec per loop
$ python -m timeit -s 'import inspect, sys' 'sys._getframe().f_code.co_name'
10000000 loops, best of 3: 0.135 usec per loop

Update 08/2021 (original post was written for Python2.7)

Python 3.9.1 (default, Dec 11 2020, 14:32:07)
[GCC 7.3.0] :: Anaconda, Inc. on linux

python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][0].f_code.co_name'
500 loops, best of 5: 390 usec per loop
python -m timeit -s 'import inspect, sys' 'inspect.stack()[0][3]'
500 loops, best of 5: 398 usec per loop
python -m timeit -s 'import inspect, sys' 'inspect.currentframe().f_code.co_name'
2000000 loops, best of 5: 176 nsec per loop
python -m timeit -s 'import inspect, sys' 'sys._getframe().f_code.co_name'
5000000 loops, best of 5: 62.8 nsec per loop
Alex Granovsky
  • 2,996
  • 1
  • 15
  • 10
  • 13
    `inspect.currentframe()` seems a good tradeoff between execution time and use of private members – FabienAndre Aug 27 '16 at 09:44
  • 1
    @mbdevpl My numbers are 1.25ms, 1.24ms, 0.5us, 0.16us normal (nonpythonic :) ) seconds accordingly (win7x64, python3.5.1) – Antony Hatchkins Jan 05 '17 at 20:55
  • Just so no one thinks@mbdevpl is crazy :) - I submitted an edit for the output of the 3rd run, since it didn't make any sense. No idea if the result should've been `0.100 usec` or `0.199 usec` but either way - hundreds of times faster than options 1 and 2, comparable with option 4 (though Antony Hatchkins found option 3 three times faster than option 4). – dwanderson Jan 13 '17 at 16:03
  • 6
    I use `sys._getframe().f_code.co_name` over `inspect.currentframe().f_code.co_name` simply because I have already imported the `sys` module. Is that a reasonable decision? (considering the speeds appear quite similar) – PatrickT Jun 06 '20 at 19:05
  • 6
    This is fullblow answer and should be the accepted one in my view – Nam G VU Aug 27 '20 at 04:29
  • 3
    We can use `sys._getframe().f_back.f_code.co_name` to get the callers name too – Fernando Crespo Apr 28 '21 at 18:57
  • `sys._getframe().f_back.f_code.co_name` does not work at all in python 3.9 – Manifest Man Aug 13 '21 at 11:56
  • What is the purpose of `from __future__ import print_function`? Works fine for me without it. – MEMark Aug 20 '21 at 07:35
  • @MEMark this is legacy ;-) – Alex Granovsky Aug 25 '21 at 12:07
  • using inspect is very slow like the answer suggested, I think everyone really needs to consider this. – Abdelsalam Hamdi Sep 26 '21 at 07:45
  • @ManifestMan You can use `sys._getframe(1)` to access the higher frame (tested in Python 3.10) but `sys._getframe().f_back` works for me too. – pabouk - Ukraine stay strong Jun 03 '22 at 22:06
  • The last option `sys._getframe()` invokes a private method from an other context, means you invoke a private method publicly. Notice, the `_` which is an indication for a private-accessed method. Please don't invoke it publicly! – Roland Jun 06 '23 at 09:19
71
functionNameAsString = sys._getframe().f_code.co_name

I wanted a very similar thing because I wanted to put the function name in a log string that went in a number of places in my code. Probably not the best way to do that, but here's a way to get the name of the current function.

CodeZombie
  • 5,367
  • 3
  • 30
  • 37
Ron Davis
  • 1,259
  • 11
  • 13
  • 3
    Totally working, just using sys, don't need to load more modules, but not soo easy to remember it :V – m3nda Jun 13 '16 at 20:29
  • Logging supports function name in the message format string. – Gringo Suave Feb 03 '23 at 18:09
  • `_` indicates a private method. Please don't invoke them publicly. You maybe want to use `inspect.currentframe()` instead which is a public method. – Roland Jun 06 '23 at 09:20
  • Well Roland, you may have a point, but look at the number of upvotes this answer got. Even I'd prefer to use this answer instead of `inspect`, which a person named gardah (a comment in an answer above) says consumed a lot of time. If the problem is with it being private, the developers need to be coaxed to provide a public function named `getCurrentFunctionName()` and `getParentFunctionName()`. – Nav Aug 28 '23 at 06:54
55

You can get the name that it was defined with using the approach that @Andreas Jung shows, but that may not be the name that the function was called with:

import inspect

def Foo():
   print inspect.stack()[0][3]

Foo2 = Foo

>>> Foo()
Foo

>>> Foo2()
Foo

Whether that distinction is important to you or not I can't say.

Community
  • 1
  • 1
bgporter
  • 35,114
  • 8
  • 59
  • 65
  • 4
    Same situation as with `.func_name`. Worth remembering that class names and function names in Python is one thing and variables referring to them is another. – Kos Dec 13 '12 at 11:48
  • Sometimes you may want `Foo2()` to print `Foo`. For example: `Foo2 = function_dict['Foo']; Foo2()`. In this case, Foo2 is a function pointer for perhaps a command line parser. – Harvey Jun 01 '13 at 20:02
  • What kind of speed implication does this have? – Robert C. Barth Feb 20 '14 at 20:29
  • 3
    Speed implication with regard to what? Is there a situation where you'd need to have this information in a hard realtime situation or something? – bgporter Feb 23 '14 at 15:27
37

I keep this handy utility nearby:

import inspect
myself = lambda: inspect.stack()[1][3]

Usage:

myself()
xxyzzy
  • 580
  • 4
  • 7
  • 1
    How would this be done with the alternative proposed here? "myself = lambda: sys._getframe().f_code.co_name" doesn't work (the output is ""; I think because the result is determined at definition time, not later at call time. Hmm. – NYCeyes May 22 '17 at 22:42
  • 2
    @prismalytics.io: If you call myself (myself()) and don't just use its value (myself), you'll get what you're looking for. – ijw Dec 27 '17 at 19:56
  • 1
    NYCeyes was right, the name is resolved inside the lambda and thus the result is . The sys._getframe() and inspect.currentframe() methods MUST be executed directly inside the function you want to get the name of. The inspect.stack() method works because you can specify the index 1, doing inspect.stack()[0][3] also yields . – kikones34 Nov 22 '19 at 13:16
  • Please do not use anonymous function literal (`lambda` ...) to create a named function. Define the function the normal way using `def` and avoid multiple possible problems. – pabouk - Ukraine stay strong Jun 03 '22 at 20:07
31

I guess inspect is the best way to do this. For example:

import inspect
def bar():
    print("My name is", inspect.stack()[0][3])
Yuval Pruss
  • 8,716
  • 15
  • 42
  • 67
Bjorn
  • 5,272
  • 1
  • 24
  • 35
  • 8
    Instead if using `inspect.stack()[0][3]`, use `inspect.stack()[0].function` which should be more robust even when semantics in stack traces change. – Tom Pohl Oct 07 '20 at 06:16
30

This is actually derived from the other answers to the question.

Here's my take:

import sys

# for current func name, specify 0 or no argument.
# for name of caller of current func, specify 1.
# for name of caller of caller of current func, specify 2. etc.
currentFuncName = lambda n=0: sys._getframe(n + 1).f_code.co_name


def testFunction():
    print "You are in function:", currentFuncName()
    print "This function's caller was:", currentFuncName(1)    


def invokeTest():
    testFunction()


invokeTest()

# end of file

The likely advantage of this version over using inspect.stack() is that it should be thousands of times faster [see Alex Melihoff's post and timings regarding using sys._getframe() versus using inspect.stack() ].

Gino
  • 1,593
  • 17
  • 22
29

I found a wrapper that will write the function name

from functools import wraps

def tmp_wrap(func):
    @wraps(func)
    def tmp(*args, **kwargs):
        print func.__name__
        return func(*args, **kwargs)
    return tmp

@tmp_wrap
def my_funky_name():
    print "STUB"

my_funky_name()

This will print

my_funky_name

STUB

Community
  • 1
  • 1
cad106uk
  • 471
  • 5
  • 5
  • 1
    As a decorator noob, I wonder if there is a way to access func.__name__ inside the context of my_funky_name (so I can retrieve its value and use it inside my_funky_name) – cowbert Jun 29 '17 at 21:57
  • The way to do that inside the my_funky_name function is `my_funky_name.__name__`. You could pass the func.__name__ into the function as a new parameter. func(*args, **kwargs, my_name=func.__name__). To get your decorators name from inside your function, I think that would require using inspect. But getting the name of the function controlling my function within my running function ... well that just sounds like the start of a beautiful meme :) – cad106uk Jul 02 '17 at 13:00
20

print(inspect.stack()[0].function) seems to work too (Python 3.5).

Pierre Voisin
  • 661
  • 9
  • 11
20

I am not sure why people make it complicated:

import sys 
print("%s/%s" %(sys._getframe().f_code.co_filename, sys._getframe().f_code.co_name))
karthik r
  • 989
  • 13
  • 11
  • 2
    maybe because you're using private function of sys module, outside of it. In general it is considered a bad practice, ain't it? – tikej Apr 14 '20 at 11:39
  • 2
    @tikej, the usage agrees to the best practice stated here: https://docs.quantifiedcode.com/python-anti-patterns/correctness/accessing_a_protected_member_from_outside_the_class.html – karthik r Apr 15 '20 at 01:30
19

Here's a future-proof approach.

Combining @CamHart's and @Yuval's suggestions with @RoshOxymoron's accepted answer has the benefit of avoiding:

  • _hidden and potentially deprecated methods
  • indexing into the stack (which could be reordered in future pythons)

So I think this plays nice with future python versions (tested on 2.7.3 and 3.3.2):

from __future__ import print_function
import inspect

def bar():
    print("my name is '{}'".format(inspect.currentframe().f_code.co_name))

Update: tested on 3.7.10, 3.8.10, and 3.9.5

hobs
  • 18,473
  • 10
  • 83
  • 106
19

Use __name__ attribute:

# foo.py
def bar():
    print(f"my name is {bar.__name__}")

You can easily access function's name from within the function using __name__ attribute.

>>> def bar():
...     print(f"my name is {bar.__name__}")
...
>>> bar()
my name is bar

I've come across this question myself several times, looking for the ways to do it. Correct answer is contained in the Python's documentation (see Callable types section).

Every function has a __name__ parameter that returns its name and even __qualname__ parameter that returns its full name, including which class it belongs to (see Qualified name).

Georgy
  • 12,464
  • 7
  • 65
  • 73
Vagiz Duseev
  • 702
  • 1
  • 7
  • 12
  • 15
    What would be the point of this if the function name "bar" would have to be known already to perform this? – Stephen B Jul 22 '21 at 13:33
  • 8
    @PyNoob: After renaming `bar` to `foo`, `print('bar')` happily prints (incorrectly) "bar", whereas `print(bar.__name__)` fails. – Gerhard Oct 22 '21 at 21:47
  • 1
    To elaborate on @Gerhard's comment - this is useful to protect yourself later if you rename functions, because the IDE will find this reference more easily. – Ken Williams Dec 27 '22 at 20:07
16
import inspect

def whoami():
    return inspect.stack()[1][3]

def whosdaddy():
    return inspect.stack()[2][3]

def foo():
    print "hello, I'm %s, daddy is %s" % (whoami(), whosdaddy())
    bar()

def bar():
    print "hello, I'm %s, daddy is %s" % (whoami(), whosdaddy())

foo()
bar()

In IDE the code outputs

hello, I'm foo, daddy is

hello, I'm bar, daddy is foo

hello, I'm bar, daddy is

Lee
  • 526
  • 3
  • 9
14
import sys

def func_name():
    """
    :return: name of caller
    """
    return sys._getframe(1).f_code.co_name

class A(object):
    def __init__(self):
        pass
    def test_class_func_name(self):
        print(func_name())

def test_func_name():
    print(func_name())

Test:

a = A()
a.test_class_func_name()
test_func_name()

Output:

test_class_func_name
test_func_name
nordborn
  • 457
  • 4
  • 4
11

This is pretty easy to accomplish with a decorator.

>>> from functools import wraps

>>> def named(func):
...     @wraps(func)
...     def _(*args, **kwargs):
...         return func(func.__name__, *args, **kwargs)
...     return _
... 

>>> @named
... def my_func(name, something_else):
...     return name, something_else
... 

>>> my_func('hello, world')
('my_func', 'hello, world')
Jeff Laughlin
  • 311
  • 3
  • 3
9

You can use a decorator:

def my_function(name=None):
    return name

def get_function_name(function):
    return function(name=function.__name__)

>>> get_function_name(my_function)
'my_function'
Douglas Denhartog
  • 2,036
  • 1
  • 16
  • 23
  • 2
    How is that answering the poster's question? Can you expand this to include an example how the function name is known from within the function? – parvus Mar 18 '19 at 12:37
  • @parvus: my answer _as is_ is an example that demonstrates an answer to OP's question – Douglas Denhartog Mar 22 '19 at 04:01
  • Ok, my_function is the random user's function of the OP. Blame this to my lack of understanding of decorators. Where the @? How will this work for functions whose arguments you don't want to adapt? How I understand your solution: when I want to know the function name, I have to append it with @get_function_name, and add the name argument, hoping it is not already there for another purpose. I'm likely missing something, sorry for that. – parvus Mar 29 '19 at 06:41
  • Without starting my own python course inside a comment: 1. functions are objects; 2. you could attach a name attribute to the function, print/log the name, or do any number of things with the "name" inside the decorator; 3. decorators can be attached multiple ways (e.g. @ or in my example); 4. decorators can use @wraps and/or be classes themselves; 5. I could go on, but, happy programming! – Douglas Denhartog Mar 30 '19 at 15:57
  • 3
    This just looks like a convoluted way to get to the `__name__` attribute of a function. The usage requires knowing the thing you are trying to get, which doesn't seem very useful to me in simple cases where functions aren't defined on the fly. – Avi May 14 '19 at 17:55
  • 1
    This is rather a non-answer. You just hint the very basic idea. The real usable execution of the idea is in the [answer by Jeff Laughlin](https://stackoverflow.com/a/61788034/320437) – pabouk - Ukraine stay strong Jun 03 '22 at 14:49
  • Downvoted - this doesn't answer the original question, and doesn't do what it states it's doing. There are some building blocks here that could be used to construct an answer, but even then, not as useful an answer as others already given. – Ken Williams Dec 27 '22 at 20:10
7

I suggest not to rely on stack elements. If someone use your code within different contexts (python interpreter for instance) your stack will change and break your index ([0][3]).

I suggest you something like that:

class MyClass:

    def __init__(self):
        self.function_name = None

    def _Handler(self, **kwargs):
        print('Calling function {} with parameters {}'.format(self.function_name, kwargs))
        self.function_name = None

    def __getattr__(self, attr):
        self.function_name = attr
        return self._Handler


mc = MyClass()
mc.test(FirstParam='my', SecondParam='test')
mc.foobar(OtherParam='foobar')
Genschi
  • 138
  • 1
  • 5
6

I do my own approach used for calling super with safety inside multiple inheritance scenario (I put all the code)

def safe_super(_class, _inst):
    """safe super call"""
    try:
        return getattr(super(_class, _inst), _inst.__fname__)
    except:
        return (lambda *x,**kx: None)


def with_name(function):
    def wrap(self, *args, **kwargs):
        self.__fname__ = function.__name__
        return function(self, *args, **kwargs)
return wrap

sample usage:

class A(object):

    def __init__():
        super(A, self).__init__()

    @with_name
    def test(self):
        print 'called from A\n'
        safe_super(A, self)()

class B(object):

    def __init__():
        super(B, self).__init__()

    @with_name
    def test(self):
        print 'called from B\n'
        safe_super(B, self)()

class C(A, B):

    def __init__():
        super(C, self).__init__()

    @with_name
    def test(self):
        print 'called from C\n'
        safe_super(C, self)()

testing it :

a = C()
a.test()

output:

called from C
called from A
called from B

Inside each @with_name decorated method you have access to self.__fname__ as the current function name.

6

I recently tried to use the above answers to access the docstring of a function from the context of that function but as the above questions were only returning the name string it did not work.

Fortunately I found a simple solution. If like me, you want to refer to the function rather than simply get the string representing the name you can apply eval() to the string of the function name.

import sys
def foo():
    """foo docstring"""
    print(eval(sys._getframe().f_code.co_name).__doc__)
John Forbes
  • 1,248
  • 14
  • 19
5

Sincesys._getframe().f_back.f_code.co_name does not work at all in python 3.9, following could be used from now:

from inspect import currentframe


def testNameFunction() -> str:
    return currentframe().f_back.f_code.co_name


print(f'function name is {testNameFunction()}(...)')

Result:

function name is testNameFunction(...)
Manifest Man
  • 873
  • 10
  • 16
  • 1
    Except that the question is not about line numbers. – MEMark Aug 20 '21 at 07:35
  • @MEMark i updated my answer accordently, you should have noticed the textual error, but i have also investigate the issue, and i came to the conclusion, that the call `sys._getframe().f_back.f_code.co_name` works allthough the IDE PyCharm does not recognize it **Cannot find reference '_getframe' in 'sys.pyi | sys.pyi'**. That,s why i wrote that answer before. – Manifest Man Aug 20 '21 at 10:30
  • @MEMark here is the post, i wrote on this issue : `https://stackoverflow.com/q/68772415/5667103` – Manifest Man Aug 20 '21 at 10:42
4

It seems from all the answer above that use the inspect library, all are writing something like:

import inspect

inspect.stack()[0][3]

But, since the return of inspect.stack()[0] is a NamedTuple of the form:

FrameInfo(frame=<frame at 0x103578810, file '<stdin>', line 1, code <module>>, filename='<stdin>', lineno=1, function='<module>', code_context=None, index=None)

One can simply call by the name, i.e. inspect.stack()[0].function

A small dummy example can be seen here:

    def test_train_UGRIZY_noZ(self, architecture, dataset, hyperrun, wloss):
        log.warning(f"{inspect.stack()[0].function} -- Not Implemented Yet")
        pass

Which when run prints:

WARNING - test_train_UGRIZY_noZ -- Not Implemented Yet
tallamjr
  • 1,272
  • 1
  • 16
  • 21
  • I like being able to use `.function` to reference the function name. Unfortunately the timings for this option are still in the microsecond range, as opposed to the nanosecond range for `inspect.currentframe().f_code.co_name` and `sys._getframe().f_code.co_name` as in Alex Granovsky's answer. Also in the microsecond range: `inspect.getframeinfo(inspect.currentframe()).function`, but about 2x as fast as `inspect.stack()`. – hlongmore Feb 26 '23 at 09:25
2

I like the idea of using a decorator but I'd prefer to avoid touching the function arguments. Hence, I'm providing yet another alternative:

import functools

def withname(f):
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        global __name
        __saved_name = globals().get("__name")
        __name = f.__name__
        ret = f(*args, **kwargs)
        __name = __saved_name
        return ret
    return wrapper

@withname
def f():
    print(f"in f: __name=={__name}")
    g()
    print(f"back in f: __name=={__name}")

@withname
def g():
    print(f"in g: __name=={__name}")

We need to save and restore __name when calling the function as consequence of it being a global variable. Calling f() above produces:

in f: __name==f
in g: __name==g
back in f: __name==f

Unfortunately, there is no alternative to the global variable if we don't change the function arguments. Referencing a variable, that is not created in the context of the function, will generate code that would look for a global variable:

>>> def f(): print(__function__)
>>> from dis import dis
>>> dis(f)
  1           0 LOAD_GLOBAL              0 (print)
              2 LOAD_GLOBAL              1 (__function__)
              4 CALL_FUNCTION            1
              6 POP_TOP
              8 LOAD_CONST               0 (None)
             10 RETURN_VALUE
olivecoder
  • 2,858
  • 23
  • 22
1
str(str(inspect.currentframe())).split(' ')[-1][:-1]
Harry Kearney
  • 99
  • 2
  • 3
1
import inspect


def method_name():
    return inspect.stack()[1][3]


def method_name_caller():
    return inspect.stack()[2][3]


def asdf():
    print(method_name_caller())
    print(method_name())


def asdf2():
    print(method_name_caller())
    print(method_name())
    asdf()
seunggabi
  • 1,699
  • 12
  • 12
1

implementing your own decorator

#mydecorators.py

def resolve_function(func):
    #in case annotated func is an staticmethod
    if isinstance(func,staticmethod):
        return func.__func__
    return func

def print_my_name(func):
    def function_caller(*args,**kwargs):
        _func = resolve_function(func)
        print("my name is: %s" %_func.__name__)
        return _func(*args,**kwargs)
    return function_caller

then use it

#foo.py

from mydecorators import *

@print_my_name
def bar():
    #do something else
    pass
#in terminal: my name is: bar
Adán Escobar
  • 1,729
  • 9
  • 15
0

@jeff-laughlin's answer is beautiful. I have modified it slightly to achieve what I think is the intent: to trace out the execution of functions, and also to capture the list of arguments as well as the keyword arguments. Thank you @jeff-laughlin!

from functools import wraps                                                                                                                                                                                                     
import time                                                                                                                                                                                                                     
                                                                                                                                                                                                                                
def named(func):                                                                                                                                                                                                                
    @wraps(func)                                                                                                                                                                                                                
    def _(*args, **kwargs):                                                                                                                                                                                                     
        print(f"From wrapper function: Executing function named: {func.__name__}, with arguments: {args}, and keyword arguments: {kwargs}.")                                                                                    
        print(f"From wrapper function: {func}")                                                                                                                                                                                 
        start_time = time.time()                                                                                                                                                                                                
        return_value = func(*args, **kwargs)                                                                                                                                                                                    
        end_time = time.time()                                                                                                                                                                                                  
        elapsed_time = end_time - start_time                                                                                                                                                                                    
        print(f"From wrapper function: Execution of {func.__name__} took {elapsed_time} seconds.")                                                                                                                              
        return return_value                                                                                                                                                                                                     
    return _                                                                                                                                                                                                                    
                                                                                                                                                                                                                                
@named                                                                                                                                                                                                                          
def thanks(message, concepts, username='@jeff-laughlin'):                                                                                                                                                                       
    print(f"From inner function: {message} {username} for teaching me about the {concepts} concepts of closures and decorators!")                                                                                               
                                                                                                                                                                                                                                
thanks('Thank you', 'two', username='@jeff-laughlin')                                                                                                                                                                           
print('-'*80)                                                                                                                                                                                                                   
thanks('Thank you', 'two', username='stackoverflow')
print(thanks) 

From wrapper function: Executing function named: thanks, with arguments: ('Thank you', 'two'), and keyword arguments: {'username': '@jeff-laughlin'}.

From wrapper function: <function thanks at 0x7f13e6ceaa60>

From inner function: Thank you @jeff-laughlin for teaching me about the two concepts of closures and decorators!

From wrapper function: Execution of thanks took 2.193450927734375e-05 seconds.

--------------------------------------------------------------------------------

From wrapper function: Executing function named: thanks, with arguments: ('Thank you', 'two'), and keyword arguments: {'username': 'stackoverflow'}.

From wrapper function: <function thanks at 0x7f13e6ceaa60>

From inner function: Thank you stackoverflow for teaching me about the two concepts of closures and decorators!

From wrapper function: Execution of thanks took 7.152557373046875e-06 seconds.
<function thanks at 0x7f13e6ceaca0>

What is most surprising to me is that there is a way to intercept functions at runtime, inspect them, and take some actions based on this. The other surprising thing is the memory address of the inner function was the same both times. Does anyone know why this is? I have a ways to go before I can understand this decorator/closure magic.

Pritam Dodeja
  • 177
  • 1
  • 8
  • Decorator works in roughly the same way as replacing all your `thanks()` calls to `named(thanks)()`. So the two `thanks()` calls are targeting the same function. The wrapper function (underscore (`_`)) is dynamically instantiated (and soon destroied) everytime. – Cychih Oct 13 '22 at 13:00
  • By the way, if you put some `print(_)` calls before `return _`, you will see the output upon every `@named` call. `print()` can be added between `def` for better understanding. In Python, almost all things happen in run time, including function "declarations", which creates a function object and registers it to current namespace. – Cychih Oct 13 '22 at 13:27