strange behaviour of a list of functions - python

Id like to define a list of functions by a list of parameters, to actually compose a potential from a finite number of monopoles and dipoles.
Functions in python list, however, for some reason do not behave as expected (by me).
The following example:
def function(coefficients):
phi = []
i = 0
for y in coefficients:
f = lambda x: y*(x**i)
i += 1
phi.append(f)
print f(2)
return phi
> phi = function([1,2,3])
2
8
24
> k0 = phi[0]
> k1 = phi[1]
> k2 = phi[2]
> print k0(2), k1(2), k2(2)
24 24 24
Always the last function in the list is retrieved independent of which function is picked from the list.
Any ideas?
Thanks in advance.

The lambda function holds a reference to the variable y, because of the closure property. So, all the functions have the reference to the same object and they will get the last value of y during the invocation. To fix this,
f = lambda x,y=y,i=i: y*(x**i)
Now, we have created another parameter y and that has the default value y. It makes the function depend on a local reference now. So, even if the value of y changes in the loop, the lambda function no longer refers to that y.
Note: The same explanation is applicable to the variable i as well.
To make things very clear, I would recommend using different variable names
f = lambda x, z=y, j=i: z * (x ** j)
Note: Your original output was 2 8 24. That was because, when you invoke the function, i got incremented and I don't think that is what you really wanted. The actual output should have been 1 4 12.

Related

What does lambda x=x mean?

I am new to python and I am trying to understand the logic here:
What does lambda x=x mean? in the complete function below:
tables = [lambda x=x: x*10 for x in range(1, 11)]
for table in tables:
print(table())
There are two different variables named x here.
The first one is the parameter, which is a local variable in the function being defined.
The second is a variable visible in the scope where the function is being defined. Its value is stored as the default value to use for the first variable x if no argument is passed when the function is called.
It is far less confusing if you just use two different names for the two different variables:
tables = [lambda x=y: x*10 for y in range(1, 11)]
This is the short way of writing
tables = []
tables.append(lambda x=1: x*10)
tables.append(lambda x=2: x*10)
# etc.
Based on how each function is called in the following loop, the intention appears to be to never actually pass an argument when the function is called, and that what we really want is to automate something like
tables = []
tables.append(lambda : 1 * 10)
tables.append(lambda : 2 * 10)
# etc.
The naive approach
tables = [lambda: x*10 for x in range(1, 11)]
fails because x is a free variable in the body of the function; it does not get replaced with the value of x at the time the function is called. Doing this, you end up with a list of 10 identical function, all of which depend on the last value assigned to x before the function is called.
By binding a default value that is evaluated immediately to the parameter, you "inject" the current value of x into the function right away, so that you end up with a list of 10 different functions.
tables = [lambda y = x: x*10 for x in range(1, 11)]
for table in tables:
print(table())
The output is 10 100s printed one below the other
I an unable to understand the logic

What does i=i mean, when creating a lambda in python?

I have come across this example from Python hitchhikers guide:
def create_multipliers():
return [lambda x, i=i : i * x for i in range(5)]
The example above is the solution to some issues caused with late binding, where variables used in closures are looked up at the time the inner function is called.
What does the i=i mean and why is it making such difference?
It's actually not just for lambdas; any function that takes default parameters will use the same syntax. For example
def my_range(start, end, increment=1):
ans = []
while start < end:
ans.append(start)
start += increment
return ans
(This is not actually how range works, I just thought it would be a simple example to understand). In this case, you can call my_range(5,10) and you will get [5,6,7,8,9]. But you can also call my_range(5,10,increment=2), which will give you [5, 7, 9].
You can get some surprising results with default arguments. As this excellent post describes, the argument is bound at function definition, not at function invocation as you might expect. That causes some strange behavior, but it actually helps us here. Consider the incorrect code provided in your link:
def create_multipliers():
return [lambda x : i * x for i in range(5)]
for multiplier in create_multipliers():
print multiplier(2)
When you call multiplier(2), what is it actually doing? It's taking your input parameter, 2, and returning i * 2. But what is i? The function doesn't have any variable called i in its own scope, so it checks the surrounding scope. In the surrounding scope, the value of i is just whatever value you left it -- in this case 4. So every function gives you 8.
On the other hand, if you provide a default parameter, the function has a variable called i in its own scope. What's the value of i? Well, you didn't provide one, so it uses its default value, which was bound when the function was defined. And when the function was defined, i had a different value for each of the functions in your list!
It is a bit confusing that they've used the same name for the parameter variable as they did for the iterating variable. I suspect you could get the same result with greater readability with
def create_multipliers():
return [(lambda x, y=i: y*x) for i in range(5)]
In that case, each number in the range, will be assigned to the optional parameters of each lambda function:
def create_multipliers():
return [lambda x, i=i : i * x for i in range(5)]
lambda x, i=0
lambda x, i=1
lambda x, i=2
lambda x, i=3
lambda x, i=4
So, you can call the functions now with one parameter (because they already have the default)
for f in create_multipliers():
print(f(3))
0
3
6
9
12
Or you can call the function and give the parameter you want, that's why is optional
for f in create_multipliers():
print(f(3,2))
6
6
6
6
6
There are examples where optional parameter are needed, such as recursion
For example, square in terms of square:
square = lambda n, m=0: 0 if n==m else n+square(n,m+1)
Look that the optional parameter there is used as accumulator

why does the following code executing the lambda function instead of the f(x) above?

Title
I don't understand why the following code ignores the first function and run the second instead.
def f(x):
return 100
f = lambda x: 1 if x < 2 else x + f(x-1)
print(f(5))
the output is 15.
Thanks.
You define the first function as f. You immediately over-write that with a different function, calling that one f. This is just as if you'd run the code:
f = [1, 2, 3]
f = 7
print f
You will get only the 7; the list is lost.
The second definition of f overrides the first. Variables can't have two values. And function names are effectively just variables. When you assign to f, you overwrite the value of f, so the original function is no longer accessible.

Closures in Python - variable assignment

When I come across closure in python, from the below code, I don't understand how the values for the arguments assigned for x and y when calling the function. In Point 1, we passing argument value 5 for x. It is then assigned to x in the function. In Point 2, we passing 7 to inc5, My doubt arises here, again the value 7 should assign to x, but instead how come it is assigning to y.
def makeInc(x):
def inc(y):
return y + x
return inc
inc5 = makeInc(5) #Point 1
inc5(7) #Point 2
I can't understand why you think 7 should be assigned to x. The outer function makeInc returns the inner function inc, and it's that which you assign to inc5. So inc5 accepts the argument y, and that's what is bound to the value 7.
At Point 1 you are passing a varialbe inc5 the returning result of a makeInc function, which will be another function inc with a local variable x set to 5. Then you call for this new function and pass a 7 as its one and only parameter y.

Confusion regarding mutable and immutable data types in Python 2.7

I have read that while writing functions it is good practice to copy the arguments into other variables because it is not always clear whether the variable is immutable or not. [I don't remember where so don't ask]. I have been writing functions according to this.
As I understand creating a new variable takes some overhead. It may be small but it is there. So what should be done? Should I be creating new variables or not to hold the arguments?
I have read this and this. I have confusion regarding as to why float's and int's are immutable if they can be changed this easily?
EDIT:
I am writing simple functions. I'll post example. I wrote the first one when after I read that in Python arguments should be copied and the second one after I realized by hit-and-trial that it wasn't needed.
#When I copied arguments into another variable
def zeros_in_fact(num):
'''Returns the number of zeros at the end of factorial of num'''
temp = num
if temp < 0:
return 0
fives = 0
while temp:
temp /= 5
fives += temp
return fives
#When I did not copy arguments into another variable
def zeros_in_fact(num):
'''Returns the number of zeros at the end of factorial of num'''
if num < 0:
return 0
fives = 0
while num:
num /= 5
fives += num
return fives
I think it's best to keep it simple in questions like these.
The second link in your question is a really good explanation; in summary:
Methods take parameters which, as pointed out in that explanation, are passed "by value". The parameters in functions take the value of variables passed in.
For primitive types like strings, ints, and floats, the value of the variable is a pointer (the arrows in the following diagram) to a space in memory that represents the number or string.
code | memory
|
an_int = 1 | an_int ----> 1
| ^
another_int = 1 | another_int /
When you reassign within the method, you change where the arrow points.
an_int = 2 | an_int -------> 2
| another_int --> 1
The numbers themselves don't change, and since those variables have scope only inside the functions, outside the function, the variables passed in remain the same as they were before: 1 and 1. But when you pass in a list or object, for example, you can change the values they point to outside of the function.
a_list = [1, 2, 3] | 1 2 3
| a_list ->| ^ | ^ | ^ |
| 0 2 3
a_list[0] = 0 | a_list ->| ^ | ^ | ^ |
Now, you can change where the arrows in the list, or object, point to, but the list's pointer still points to the same list as before. (There should probably actually only be one 2 and 3 in the diagram above for both sets of arrows, but the arrows would have gotten difficult to draw.)
So what does the actual code look like?
a = 5
def not_change(a):
a = 6
not_change(a)
print(a) # a is still 5 outside the function
b = [1, 2, 3]
def change(b):
b[0] = 0
print(b) # b is now [0, 2, 3] outside the function
Whether you make a copy of the lists and objects you're given (ints and strings don't matter) and thus return new variables or change the ones passed in depends on what functionality you need to provide.
What you are doing in your code examples involves no noticeable overhead, but it also doesn't accomplish anything because it won't protect you from mutable/immutable problems.
The way to think about this is that there are two kinds of things in Python: names and objects. When you do x = y you are operating on a name, attaching that name to the object y. When you do x += y or other augmented assignment operators, you also are binding a name (in addition to doing the operation you use, + in this case). Anything else that you do is operating on objects. If the objects are mutable, that may involve changing their state.
Ints and floats cannot be changed. What you can do is change what int or float a name refers to. If you do
x = 3
x = x + 4
You are not changing the int. You are changing the name x so that it now is attached to the number 7 instead of the number 3. On the other hand when you do this:
x = []
x.append(2)
You are changing the list, not just pointing the name at a new object.
The difference can be seen when you have multiple names for the same object.
>>> x = 2
>>> y = x
>>> x = x + 3 # changing the name
>>> print x
5
>>> print y # y is not affected
2
>>> x = []
>>> y = x
>>> x.append(2) # changing the object
>>> print x
[2]
>>> print y # y is affected
[2]
Mutating an object means that you alter the object itself, so that all names that point to it see the changes. If you just change a name, other names are not affected.
The second question you linked to provides more information about how this works in the context of function arguments. The augmented assignment operators (+=, *=, etc.) are a bit trickier since they operate on names but may also mutate objects at the same time. You can find other questions on StackOverflow about how this works.
If you are rebinding the name then mutability of the object it contains is irrelevant. Only if you perform mutating operations must you create a copy. (And if you read between the lines, that indirectly says "don't mutate objects passed to you".)

Categories

Resources