Creating Functions in a Loop

Creating functions (or lambdas) in a loop (or comprehension)

You're running into a problem with late binding -- each function looks up i as late as possible (thus, when called after the end of the loop, i will be set to 2).

Easily fixed by forcing early binding: change def f(): to def f(i=i): like this:

def f(i=i):
return i

Default values (the right-hand i in i=i is a default value for argument name i, which is the left-hand i in i=i) are looked up at def time, not at call time, so essentially they're a way to specifically looking for early binding.

If you're worried about f getting an extra argument (and thus potentially being called erroneously), there's a more sophisticated way which involved using a closure as a "function factory":

def make_f(i):
def f():
return i
return f

and in your loop use f = make_f(i) instead of the def statement.

How to define a function inside a loop

All your functions are referencing the same variable me. When you try and call the function, me has the value 'bar', because that's what it was when the loop ended.

If you want me to take a different value for each definition of the function, then each function needs its own copy of the variable.

u = {}
for me in ['foo', 'bar']:
def callback(me_copy=me):
return 'I am %s' % me_copy
u[me] = callback

If you wanted, you could shorten that to:

u = {}
for me in ['foo', 'bar']:
u[me] = lambda me_copy=me: 'I am %s'%me_copy

or even

u = { me: (lambda me_copy=me: 'I am %s'%me_copy) for me in ('foo', 'bar') }

Using for loop to define multiple functions - Python

Use functools.partial combined with a dictionary in this situation.

I assume what you really want to do is more complex, since multiple functions are not necessary for this specific task.

from functools import partial

def add(x, i):
return x + i

d = {f'add{k}': partial(add, i=k) for k in range(1, 10)}

d['add3'](5) # 8

Explanation

  • It is good practice to store a variable number of related objects in a specially defined dictionary.
  • functools.partial is a higher order function which returns an input function with selected argument(s) fixed.

Polluting the namespace

From the comments, a commonly asked question goes:

OP seems to be asking if there's a better way of just doing def
add1:
... def add2:... While I agree with your solution, I don't agree
with the fact that it's the solution to the current question. -
MooingRawr

Why is using an iterator to define functions a bad idea? It seemed
like a good way to shorten code to me. Is it generally better to
define them one by one? – jessica

My short answer:

Using globals().update to pollute the namespace is a bad idea.. Hold
related variables in specially made collections. It makes sense from
any viewpoint (maintainability, calling, modifying, etc). – jpp

@BoarGules's extend answer:

It's not a good idea because it creates functions dynamically, but
they can only be called by static code (otherwise the code wouldn't
know what to call), so what's the point of them being dynamic?
Creating them dynamically makes the code hard to read (you can't
easily search for the function definition) and gives IDEs unnecessary
trouble in their efforts to help you. You save tedious, repetitive
coding, but programming sometimes entails tedious and careful
repetition. The effort and skill would be better spent speeding up the
coding (clever search/replace, macros, whatever). – BoarGules

Creating functions in a loop with names depends on the iterable

Just what you asked for.

for i in range(1, 10):
def fn(x):
return int(str(x)[i])

globals()[f"n{i}"] = fn # update global variables dictionary

#use them like
n3(123456)

But I recommend defining a function that takes both arguments and using it

def ni(i,x):
return int(str(x)[i])

ni(3, 123456)

Javascript: Creating Functions in a For Loop

The second method is a little clearer if you use a parameter name that does not mask the loop variable name:

funArr[funArr.length] = (function(val) { return function(){  return val; }})(i);

The problem with your current code is that each function is a closure and they all reference the same variable i. When each function is run, it returns the value of i at the time the function is run (which will be one more than the limit value for the loop).

A clearer way would be to write a separate function that returns the closure that you want:

var numArr = [];
var funArr = [];
for(var i = 0; i < 10; ++i){
numArr[numArr.length] = i;
funArr[funArr.length] = getFun(i);
}

function getFun(val) {
return function() { return val; };
}

Note that this is doing basically the same thing as the first line of code in my answer: calling a function that returns a function and passing the value of i as a parameter. It's main advantage is clarity.

EDIT: Now that EcmaScript 6 is supported almost everywhere (sorry, IE users), you can get by with a simpler approach—use the let keyword instead of var for the loop variable:

var numArr = [];
var funArr = [];
for(let i = 0; i < 10; ++i){
numArr[numArr.length] = i;
funArr[funArr.length] = function(){ return i; };
}

With that little change, each funArr element is a closure bound do a different i object on each loop iteration. For more info on let, see this Mozilla Hacks post from 2015. (If you're targeting environments that don't support let, stick with what I wrote earlier, or run this last through a transpiler before using.



Related Topics



Leave a reply



Submit