0

I want to create multiple functions namly func1, func2, ..., funci, in a for loop that all take different definitions. I have tested the following methods but they throw me errors. How is the proper way to do it:

What I want:

for i in range(1, 10):
    func1(x, args1):
        do something

    func2(x, args2):
        do something

What I did but didn't work:

for ii in range(1,10):
    def globals()["func{}".format(ii)](t, "args{}".format(ii))

for ii in range(1,10):
    def "func{}".format(ii)(t, "args{}".format(ii))

For the question I have I found only the following link which does not work for my purpose.

How do I loop through functions with similar names in python?

Ali Wali
  • 179
  • 2
  • 8
  • Are you allowed to use lambda-s? https://www.educative.io/blog/python-lambda-functions-tutorial ; are you allowed to learn [GNU guile](https://www.gnu.org/software/guile/) - semantically very Python-like, but with a very different and simpler syntax. Are you allowed to read [SICP](https://mitpress.mit.edu/sites/default/files/sicp/index.html) ? – Basile Starynkevitch Apr 21 '21 at 14:32
  • 6
    Why do you want to do this? – jarmod Apr 21 '21 at 14:33
  • 2
    I don't think you can reasonably create function definitions dynamically with respect to the argument names. I'd be interested in how to do it unreasonably, but it is very likely that whatever you are trying to do has a better solution. – timgeb Apr 21 '21 at 14:35
  • @BasileStarynkevitch yes, anything that works for my purpose is fine. – Ali Wali Apr 21 '21 at 14:36
  • Then I recommend to read SICP and use GNU guile. This requires a few weeks of work. Another possibility could be to learn [Ocaml](http://ocaml.org/) – Basile Starynkevitch Apr 21 '21 at 14:36
  • @jarmod I have multiple functions with different arguments, bounds, initial parameters, etc. The best way is to define all in a for loop I believe. – Ali Wali Apr 21 '21 at 14:37
  • 2
    I'm murderously sure it's not the best way. For starters, why are the argument names so important? – timgeb Apr 21 '21 at 14:38
  • 2
    Asking to create a bunch of functions with sequential names in a loop is like asking to create a bunch of variables with sequential names in a loop, and the answer to the second question is _don't, use a `list` or `dict` instead_. If you can define these functions in a `for` loop, you can define a single function that takes the "function number" as an argument and does different things based on that argument. Argument names are unimportant, but can be passed through `**kwargs`. – Pranav Hosangadi Apr 21 '21 at 14:42
  • Use a dict with lambdas. – The.B Apr 21 '21 at 15:27
  • Dont forget to accept the answer that helped you most / was written the best using the grey checkmark on the left of it ! – TheEagle Apr 21 '21 at 20:09

3 Answers3

1

I think this answer using inspect's signature function does what you want. If it doesn't, or you think it's too complicated, the only way I can think of is to use eval:

for i in range(1, 10):
    eval(f"""def func{i}(arg{i}):
    print(arg{i})""")

Please read here why you shouldn't do that.

TheEagle
  • 5,808
  • 3
  • 11
  • 39
  • I upvoted this answer because it is the only one doing what OP is asking for. I also think nobody should ever use this code. – timgeb Apr 22 '21 at 08:01
  • 1
    @timgeb well, I provided a link exlaining why oone shouldn't, and then, it's the OP's decision whether to use it all the same or not have this functionality. – TheEagle Apr 22 '21 at 08:21
0

It's not clear what exactly you're trying to accomplish here.

This is a simple example of a way to define a list of functions dynamically:

fns = [lambda:char for char in string.ascii_lowercase]

(obviously, I've defined some very simple functions to focus on the creation aspect)

Based on discussion in the comments thread, I believe what the OP is looking for is to pass a list of functions to an optimizing function (specifically, scipy.optimize_least_squares)

The most important thing to know here is that the names of the functions in the local namespace do not matter, what matters is that the functions are passed to the optimizing function with the correct parameter names. (they will then have the correct names in the namespace of the called function, which is what you want)

However, if you want to dynamically generate the keyword arguments for a function call, the easiest way to do that is to create a dict and then use the ** operator to unpack it:

kwargs = some_func_returning_a_dict_of_kwargs()
result = scipy.optimize_least_squares(**kwargs)
Jon Kiparsky
  • 7,499
  • 2
  • 23
  • 38
  • I have multiple functions with more or less the same arguments that need to be curve fitted. Since all the initial parameters, bounds, etc. are different for these function I am trying to create a loop that can define different function names and suit my purpose of giving multiple names. – Ali Wali Apr 21 '21 at 14:46
  • @AliWali where do these names come from and who uses them and for what? – Jon Kiparsky Apr 21 '21 at 14:51
  • They are used in scipy least square that takes different names, Here is an example for func9: Cauchy_dca = sc.optimize.least_squares( fun=lambda parms, time, rate: q9 - func9(t9, parms9), bounds=bounds9, x0=parm_init9, args=(t, q), loss='cauchy', f_scale=f_scale9) – Ali Wali Apr 21 '21 at 14:56
  • If you're passing these functions as arguments to a scipy function, then the names of the functions in the current namespace don't matter, but I'll update my answer with a suggestion that may help – Jon Kiparsky Apr 21 '21 at 15:01
0

Use a dict and put lambda functions as value with arguments, e.g:

d = {}

for i in range(0,101):
    f = "func{0}".format(i)
    if  f not in d:
        d[f] = lambda x, args: x * args

print(d['func1'](10,10))
print(d['func2'](10,20))
print(d['func3'](10,30))

This will give you each key calling that function with different arguments. you don't need to call the args{0}.format(i) to give you different name, you can keep track of them with your keys. this is preferable way when readability counts.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
The.B
  • 361
  • 2
  • 11