-1

Let's say that I want to create a list of functions funclist, such that the fonctions of this list behave that way:

funclist[0](x) = 0
funclist[1](x) = x
funclist[2](x) = 2*x
funclist[3](x) = 3*x

Assuming that the list is too long (say of size 100) to define those functions one-by-one, a natural way to write this would be:

funclist = []
for k in range(100):
    f = lambda x: k*x
    funclist.append(f)

The problem is that this approach does not work, because of the way Python interprets local variables within function definitions. This is a problem I saw coming back often on this website, for instance here or here. Unfortunately, those users want to define constant functions, so the proposed solution (which is to define a local copy of the iterable variable k) do not work for me (it is also the same solution proposed in the Python FAQ). For instance, the following syntax does not work:

funclist = []
for k in range(100):
    f = lambda local_k=k, x: local_k*x
    funclist.append(f)

A solution I found is to do:

funclist = []
for k in range(10):
    def f(x, product_factor=k): return x*product_factor
    funclist.append(f)

which works as I want. I could also achieve the same result with the partial function:

from functools import partial 

def product(x,product_factor): return x*product_factor
funclist = []
for k in range(10):
    funclist.append( partial(product, product_factor=k) )

So my question is : is there any way to achieve this result with lambda functions only? Or the fact that I have a "moving parameter" dooms me to define in a way or an other a function of two variables? Let me precise that I am asking this because I am trying to understand better how functions and currying work in Python.

Guillaume
  • 257
  • 1
  • 8
  • 1
    The lambda solution does not work because of the order of arguments. You flipped those for the named functions, which you could have done for the lambda functions just as well. – user2390182 Apr 22 '20 at 15:58
  • Ohhh yes of course! Thank you! This is the answer I was looking for, don't you want to claim the answer? – Guillaume Apr 22 '20 at 16:01
  • Sure =) glad to help – user2390182 Apr 22 '20 at 16:02
  • "For instance, the following syntax does not work:" This is because default arguments must come after non-default arguments. "is there any way to achieve this result with lambda functions only? " What's wrong with the next code block, which you say is "a solution" and "works as [you] want"? Voting to close as not reproducible, because I don't see how there is an identifiable problem in the OP to ask a question about. – Karl Knechtel Aug 19 '22 at 03:57
  • As you say, my problem was the order of the arguments. This was already pointed out in a previous comment, and in an answer that I accepted 2 years ago. What is the problem with this? – Guillaume Aug 30 '22 at 09:43

2 Answers2

1

Your lambda approach does not work because you have a keyword argument before a positional one. Just flip the order as you did in the working solutions:

funclist = [(lambda x, local_k=k: local_k*x) for k in range(100)]

>>> funclist[3](8)
24
>>> funclist[5](9)
45
user2390182
  • 72,016
  • 6
  • 67
  • 89
1

The initial problem is that k was only evaluated one time, at the end of the loop, while here the function creation, the call to get_function, triggers the evaluation of k every time.

Of course your solution with a default argument also works, it’s apparently the most common solution for this problem

Now you said it didn’t work with the lamdba, it’s simply because you put the default argument first :

funclist = []
for k in range(100):
    f = lambda  x, local_k=k,: local_k*x
    funclist.append(f)


print(funclist[10](3)) #30
print(funclist[99](3)) # 297
print(funclist[0](3)) # 0

works just fine