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. -
MooingRawrWhy 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
All Combinations of a List of Lists
Select Dataframe Rows Between Two Dates
How to Print a Single Backslash
Bare Asterisk in Function Arguments
Converting Between Datetime, Timestamp and Datetime64
How to Concatenate Text Files in Python
Why Is "1000000000000000 in Range(1000000000000001)" So Fast in Python 3
How to Convert an Rgb Image into Grayscale in Python
How to Call a Function from Another .Py File
Flask View Return Error "View Function Did Not Return a Response"
Convert Hex String to Integer in Python
Annotate Bars With Values on Pandas Bar Plots
How to Write the Fibonacci Sequence
Permutations With Unique Values