1

I just wrote a small function that returns its own arguments as a dict:

from inspect import signature

class MyClass:
    def MyFunc(self, thing1=0, thing2=0, thing3=0, thing4="", thing5=""):
        P = {}
        for p in list(signature(self.MyFunc).parameters):
            P[p] = eval(p)

        return P   

Setting aside why anyone would want to do that (and accepting that I've distilled a very simple example out of a broader context to explore a very specific question), there's an explicit reference self.MyFunc there.

I've seen complicated ways of avoiding that like:

globals()[inspect.getframeinfo(inspect.currentframe()).function]

and

globals()[sys._getframe().f_code.co_name]

but I wonder if there's something like the anonymous super() construct Python offers to reference the method of the same name in a parent class, that works for elegantly permitting a function to refer to itself, anonymously, i.e. without having to name itself.

I suspect not, that there is no way to do this as of Python 3.8. But thought this a worthwhile question to table and explore and invite correction of my suspicion on.

martineau
  • 119,623
  • 25
  • 170
  • 301
Bernd Wechner
  • 1,854
  • 1
  • 15
  • 32
  • 2
    Even `super` requires you to provide the method name explicitly (and it's not quite designed "to reference the method of the same name in a parent class"). – user2357112 Mar 25 '20 at 01:04
  • Does this answer your question? [Determine function name from within that function (without using traceback)](https://stackoverflow.com/questions/5067604/determine-function-name-from-within-that-function-without-using-traceback) – Iain Shelvington Mar 25 '20 at 01:12
  • As an aside, I might do something like `local_vars = locals()` outside the loop and then `P[p] = local_vars[p]` – juanpa.arrivillaga Mar 25 '20 at 01:17
  • @user2357112 supports Monica, with all due respect you misunderstood me there. super() does not require you to name the class it refers to. Indeed if you want a method in that class yes you need to name it. But since Python 3 the type defaults to the class it's used within. To wit it became much cleaner to use. – Bernd Wechner Mar 25 '20 at 01:40

3 Answers3

3

No such construct exists. Code in a function has no special way to refer to that function.

Execution of a function doesn't actually involve the function itself, after initial startup. After startup, all that's needed from the function is the code object, and that's the only part the stack frame keeps a reference to. You can't recover the function from just the code object - many functions can share the same code object.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • Much as I thought. Thanks for confirming. Though as a lawyer once explained to me the challenge with any "does not exist" is a hard one (because proof is only possible that it exists if it does, not existing is harder) and should perhaps be accompanied with some qualification as to the experience base the claim derives from. That said I concur that a language spec is generally much smaller than the body of law and so am prepared to take the claim on the understanding you have exhaustive familiarity with the Python spec. Thanks. – Bernd Wechner Mar 25 '20 at 01:45
1

You can do it with a decorator that adds the parameter list to those passed to the method.

The same approach could be extended into a class decorator that did it to some or all of the methods of the class.

Here's an example implementation of the single-method decorator:

from inspect import signature

def add_paramlist(func):
    paramlist = list(signature(func).parameters)
    try:
        paramlist.remove('paramlist')
    except ValueError as exc:
        raise RuntimeError(f'"paramlist" argument not declareed in signature of '
                           f'{func.__name__}() method') from exc
    def wrapped(*args, **kwargs):
        return func(paramlist, *args, **kwargs)
    return wrapped


class MyClass:
    @add_paramlist
    def MyFunc(paramlist, self, thing1=0, thing2=0, thing3=0, thing4="", thing5=""):
        P = {}
        for p in paramlist:
            P[p] = eval(p)

        return P


from pprint import pprint

inst = MyClass()
res = inst.MyFunc(thing1=2, thing2=2, thing3=2, thing4="2", thing5="2")
pprint(res)

Output:

{'self': <__main__.MyClass object at 0x00566B38>,
 'thing1': 2,
 'thing2': 2,
 'thing3': 2,
 'thing4': '2',
 'thing5': '2'}
martineau
  • 119,623
  • 25
  • 170
  • 301
-1

As user2357112 says,you can't have any hack-less way to get a name of a function from within that function,but if you just want a function to return its arguments as a dict, you can use this:

class MyClass:
    def MyFunc(self,**kwargs):
        return kwargs

or if you want to use the *args:

class MyClass:
    def MyFunc(self,*args,**kwargs):
        names=["thing%d"%i for i in range(1,6)]
        for v,k in zip(args,names):
            if k in kwargs:
                raise ValueError
            else:
                kwargs[k]=v
        return kwargs

Using a hack including locals:

class MyClass:
    def MyFunc(self, thing1=0, thing2=0, thing3=0, thing4="", thing5=""):
        d=locals().copy()
        del d["self"]
        return d
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
xkcdjerry
  • 965
  • 4
  • 15