0

I am drawing dozens of functions as subplots, by storing pure functions as list. I find many different functions are considered the same. Here is a simplified example, where two cosine functions are drawn.

#!/usr/bin/env python3
import math # cos
from matplotlib import pyplot as plt # Plotting.

scale =[0.01*x for x in list(range(200))]
list_fun =[lambda t: math.cos(2*math.pi*i*t) for i in [1,2]]

data_1 =list(map(list_fun[0], scale))
data_2 =list(map(list_fun[1], scale))

fig =plt.figure( figsize=(11,5) )

ax =fig.add_subplot(1, 2, 1) # left
ax.plot( scale, data_1, label="cos 2$\pi$t" )
ax.legend()

ax =fig.add_subplot(1, 2, 2) # right
ax.plot( scale, data_2, label="cos 4$\pi$t" )
ax.legend()

plt.show()

The plot shows both $\cos (4\pi t)$ functions, but one should be $\cos (2\pi t)$. I guess the list formed by several pure functions are invalid in python, is it so? If so, is there an alternative syntax? I am new to Python so there may have been some glaring errors.

Violapterin
  • 337
  • 2
  • 14
  • 2
    Your `list_fun` lists contains two identical functions because of how you defined it. Both functions return `math.cos(2*math.pi*i*t)`, where `i` has its final value, 2 – khelwood May 30 '17 at 17:37
  • 1
    Use `lambda t, i=i: ... for i in [1,2]` to create a closure over the current value of `i`. Otherwise, `i` is simply a global (or nonlocal) variable in the body of the resulting function object, whose value is found when the function is called. – chepner May 30 '17 at 17:39
  • 2
    The old [late binding](http://python-guide-pt-br.readthedocs.io/en/latest/writing/gotchas/#id1) problem! – user2390182 May 30 '17 at 17:39
  • See [Why do lambdas defined in a loop with different values all return the same result?](https://docs.python.org/3/faq/programming.html#why-do-lambdas-defined-in-a-loop-with-different-values-all-return-the-same-result) and https://sopython.com/canon/30/why-do-my-lambda-functions-or-nested-functions-created-in-a-loop-all-use-the-las/ – PM 2Ring May 30 '17 at 17:47
  • Possible duplicate of https://stackoverflow.com/questions/12423614/local-variables-in-python-nested-functions The code in that question uses a different technique to define the functions, but it's essentially the same problem. OTOH, at first glance, it's not clear that this question is a dupe of that one, so I'm not comfortable using it as a dupe target. – PM 2Ring May 30 '17 at 17:52

1 Answers1

2

Your list_fun list contains two identical functions because of how you defined it. Both functions return math.cos(2*math.pi*i*t), where i has its final value, 2.

You could copy i to another variable to get this to work:

[lambda t, m=i: math.cos(2*math.pi*m*t) for i in [1,2]]
khelwood
  • 55,782
  • 14
  • 81
  • 108
  • This is correct! But I am still struggling to understand the evaluation order.... – Violapterin May 30 '17 at 17:49
  • 1
    @Aminopterin In khelwood's code the assignment of the default value `m=i` happens when the function is created, so `m` gets the desired value of `i` as its default value. In your code, the name `i` is looked up each time the function is called, and in this case `i` is found in the scope of the list comp where the function was created, where `i` has the value it was set to at the end of the `for` loop. – PM 2Ring May 30 '17 at 17:58