0

I think that's a python beginner question.

my goal is to pass a variable (idx) to a function that is given as argument.

I tried to wrap the function call, to pass the variable as argument, but for some reason the result is not as I expected.

Here is the code:

for idx, path in enumerate(paths):
  def _mf(t):
    return make_frame(t, idx)

  clip2 = VideoClip(_mf, False, 10)

This loop will iterate for 2 items. And the two clip will call the make_frame function respectively. But the the second argument (idx) will always be "1" for both clips. I don't understand this.

Can anybody help me? Thanks

appsthatmatter
  • 6,347
  • 3
  • 36
  • 40
  • While I look for the right dup of this question, see [the official Python FAQ](https://docs.python.org/3.4/faq/programming.html#why-do-lambdas-defined-in-a-loop-with-different-values-all-return-the-same-result), which explains this, and how to do what you want. – abarnert May 22 '15 at 21:29
  • Also, if the reason you expected a different result is that you understand lexical scoping, and came from a language like C++ where every block (compound statement, nested statement, pair of braces, …) is a scope, the difference in Python is that most blocks are _not_ a scope, only function and class definitions. (That's oversimplifying a tiny bit, but close enough.) – abarnert May 22 '15 at 21:31
  • ok, so the _mf function will be overwritten through the iteration, right? what could be the solution? i come from the java and javascript world... – appsthatmatter May 22 '15 at 21:34
  • No, you're getting a separate function named `_mf` for each iteration, each with its own separate closure—but they're both closures over the same `idx` variable, whose value is `2` by the time you call it. Both the FAQ I linked in my first comment, and the answers to the linked duplicate question, explain this far better than I can explain in a comment, and provide workarounds for when this isn't what you want. – abarnert May 22 '15 at 21:36
  • 1
    It's hard to know exactly how to explain this without knowing how much you already understand, but the short answer is that "my goal is to pass a variable" is wrong—that's exactly what you're doing, but what you actually _wanted_ is to pass _the current value of_ a variable. You can do that by passing it as a default argument value, or using a function that creates and returns new functions, or `functools.partial`, or just about anything other than actually creating a closure over the variable. – abarnert May 22 '15 at 21:39
  • @abarnert: The problem you (and the FAQ) describe occurs because the newly-created function is called much later --- after all its variants have been defined and the `for` loop is done. In this question, the `_mf` function is used (or at least handed off to some other callable) _inside_ the `for` loop --- before `idx` increments. Does this mean that his `VideoClip` (whatever it is) must be storing the reference to `_mf` and not calling it until after the `for` loop is done? – Kevin J. Chase May 22 '15 at 22:15
  • @KevinJ.Chase: Yes. (Presumably the reason `VideoClip` takes a function is so that it can call it later; what would be the point of taking a function instead of a value otherwise?) – abarnert May 22 '15 at 22:29

1 Answers1

0

You could try:

def foo(idx):
    return lambda t: make_frame(t, idx)

for idx, path in enumerate(paths):
    clip2 = VideoClip(foo(idx), False, 10)
JuniorCompressor
  • 19,631
  • 4
  • 30
  • 57
  • Since you complained elsewhere that I didn't undownvote a wrong answer you corrected, I went looking and found this one. Well, someone downvoted this, and presumably never came back. I don't know if they did so because of the same problem as in my comment, or if it's just one of those people who likes to punish anyone who answers a question that obviously should have been closed as part of a crusade. But whatever the reason, I can't undo someone else's downvote. I can remove my comment, which I'll do. – abarnert May 22 '15 at 22:31
  • @abarnert Ok i take it back. I'm sorry – JuniorCompressor May 22 '15 at 22:32