-2

Marked as solved because: The solved solution provides a good-enough solution to the problem, even though it doesn't quite solve the problem of dynamically generating the function name on-demand.

I want to generate a function which returns its own signature with parameters. Is this possible in Python? So, the function gets generated by the call itself, and returns the string which represents its own name and parameters.

Here is a simple example of what I am looking for:

class Object(object):
  #Impl here?
  pass

a = Object()
returned_string = a.foo(param1 = 1, param2 = "cheese")

print(returned_string)
#should print: a.foo(param1 = 1, param2 = "cheese")
#or     print:   foo(param1 = 1, param2 = "cheese")

Important, foo is an arbitrary name and should not be "hard-coded" but generated.

The code should allow for the following:

 print(a.doodle(param1 = 32)) #prints: doodle(param1 = 32)
 print(a.bar(param42 = "pi")) #prints: bar(param42 = "pi")

without defining further functions.

Thanks :)

BarFoo
  • 1
  • 3
  • 1
    Whats with the `class Object(object)`? Can you elaborate with an example? Maybe make a class that is reproducible with param1 and param2 – Akshay Sehgal Feb 11 '21 at 13:46
  • with `import inspect` and `inspect.stack()[0][3]` you can get the name of the current function. – Camaendir Feb 11 '21 at 13:51

3 Answers3

0

I am assuming what you mean is that once you have created an object, the function is able to return its own input parameters for that object. You can of course modify it to look like a string but what you want to return is the input parameters that are the signature of that object and funciton foo

class Object():
    def foo(self, param1, param2):
        self.param1 = param1
        self.param2 = param2
        return self.__dict__

a = Object()
returned_string = a.foo(param1 = 1, param2 = "cheese")

print(returned_string)
{'param1': 1, 'param2': 'cheese'}

Returning it as a string -

import inspect

class Object():
    def foo(self, param1, param2):
        self.param1 = param1
        self.param2 = param2
        d = self.__dict__
        f = inspect.stack()[0][3]
        return f+'('+', '.join([' = '.join([str(j) for j in i]) for i in d.items()])+')'

a = Object()
returned_string = a.foo(param1 = 1, param2 = "cheese")

print(returned_string)
foo(param1 = 1, param2 = cheese)
Akshay Sehgal
  • 18,741
  • 3
  • 21
  • 51
  • This is pretty much it, but the name "foo" should not be hard-coded, but be generated when I call a.some_name – BarFoo Feb 11 '21 at 14:06
  • @Asocia It is hard-coded in the sense that the function name is fixed (no matter what it is called). Any call a.f1, a.f2, a.f3... should return the correct result and be defined. – BarFoo Feb 11 '21 at 14:14
  • Very much like you can just add variables like: a.i_am_five = 5 – BarFoo Feb 11 '21 at 14:15
  • @BarFoo, check the part about `f = inspect.stack()[0][3]`. its not hardcoded. The `f = inspect.stack()[0][3]` returns the function name while the `self.__dict__` provides the current status of the paramters in that object. Those are sufficient to provide the "signature" you want for that object+function. – Akshay Sehgal Feb 11 '21 at 14:23
  • But the function is defined as foo. So it will always be a.foo(parameters). – BarFoo Feb 11 '21 at 14:33
  • I have marked this thread as solved now, thank you for your help :D – BarFoo Feb 11 '21 at 14:33
0

You should be able to do what you want using **kwargs. As an example:

def my_args_are(**kwargs):
    return "my_args_are({})".format(
        ", ".join("{0} = {1!r}".format(arg, val)
            for arg, val in kwargs.items()))

It works:

>>> my_args_are(param1 = 1, param2 = "cheese")
"my_args_are(param1 = 1, param2 = 'cheese')"

If you don't want to hardcode the name you can use inspect:

import inspect

def my_args_are(**kwargs):
    fname = inspect.currentframe().f_code.co_name
    argstr = ", ".join("{0} = {1!r}".format(arg, val)
                       for arg, val in kwargs.items()))
    return "{}({})".format(fname, argstr)
orlp
  • 112,504
  • 36
  • 218
  • 315
  • This is almost what I am looking for! The question left is, how can the "my_args_are" be generated? So if I call my_doodle(param1 = 1, param2 = "cheese") I get "my_doodle(param1 = 1, param2 = 'cheese')" – BarFoo Feb 11 '21 at 13:57
  • @BarFoo Way ahead of you :) Refresh the answer. – orlp Feb 11 '21 at 13:57
  • I thought there would be a way to create undefined functions on deman (like you can write a.foo = 32) – BarFoo Feb 11 '21 at 13:58
  • Ah, this still requires the explicit definition of the function my_args_are, isn't there a way to generate this function name when I call a.function_name? – BarFoo Feb 11 '21 at 14:05
  • Well, to be honest, I think I can work with this already. I will just change my expectation a little :) I will mark this as a solved. Thanks :) – BarFoo Feb 11 '21 at 14:19
  • @BarFoo Sorry, I generally don't respond to 'follow-up questions', because in my experience the goalpost keeps getting moved as the user has a self-discovery process of what they *really* want to no one else's benefit. If you get stuck anywhere feel free to ask another question on stackoverflow where I or others can help. – orlp Feb 11 '21 at 14:21
  • All cool, I will make do with the solution you offered. I will just use a call("function_name", parameters) format – BarFoo Feb 11 '21 at 14:23
0

So here's the function you want:

import inspect

class Object(object):

    def your_function(**options):
        return f"{inspect.stack()[0][3]}({', '.join([str(key) + ' = ' + str(options[key]) for key in options.keys()])})"

So if you do:

a = Object() and

a.your_function(abc="abc", ert=3)

it will return:

your_function(abc = abc, ert = 3)

Camaendir
  • 472
  • 3
  • 8