Bernd Wechner
Bernd Wechner

Reputation: 2143

Can a method reference itself anonymously?

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.

Upvotes: 1

Views: 83

Answers (3)

xkcdjerry
xkcdjerry

Reputation: 983

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

Upvotes: -1

martineau
martineau

Reputation: 123521

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'}

Upvotes: 1

user2357112
user2357112

Reputation: 281758

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.

Upvotes: 3

Related Questions