Bug or misunderstanding in Python 2.7? [duplicate] - python

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
“Least Astonishment” in Python: The Mutable Default Argument
Am I missing something here or is this really a bug? Below is a recursive function that generates a multi-dimensional list from a tuple specification, for example.
dim((2,3))
returns
[[[],[],[]],[[],[],[]]]
The only problem is that it adds to the list each time I call it, if I call it without the default parameter, if I specify the default parameter like dim((2,3),[]), then it's fine. It's saving the state of the default parameter each call! If no one can find a problem with what I'm doing I'll enter it in the python bug reporter.
cdr = lambda l : l[1:]
car = lambda l : l[0]
last = lambda x : x[-1:][0]
def dim(t, c = []):
if len(t) > 0:
i = car(t)
for j in range(i):
c.append([])
dim(cdr(t), last(c))
return c
print dim([2,3])
print dim([2,3])
print dim([2,3])
print dim([2,3])

def dim(t, c = [])
It's a bug (in your code). That c = [] part is only evaluated once during the entire program. When you call dim, c is being continuously appended to. A better approach would be this:
def dim(t, c=None):
if c is None:
c = []
...

Related

List of lambda functions does weird things [duplicate]

This question already has answers here:
Creating functions (or lambdas) in a loop (or comprehension)
(6 answers)
What do lambda function closures capture?
(7 answers)
Closed 6 months ago.
This is so weird, if I make a list of lambda functions and with a for, I print the output of each function, everything works, but if I individually print the output of a single function like the first one, it gives me the output of the last function or I don't even know what it gives me, for example:
X = [1,2,3,4]
L = []
for i in range(len(X)):
L.append(lambda x: X[i]**x)
for i in range(len(X)):
print(L[i](2))
This gives me:
1
4
9
16
That is correct, but if i want only the first one:
print(L[0](2))
# -> 16
And if I want the second one does the same, and so on, I checked the lambda functions were all different and they are. I don't know what's going on
The lambda references the global variable i, so after the for loop, i==3, computing X[3]**2:
X = [1,2,3,4]
L = []
for i in range(len(X)):
L.append(lambda x: X[i]**x)
for f in L:
print(f(2))
Output:
16
16
16
16
A way to fix is to capture the current value of global i as a local parameter i when the function is defined:
X = [1,2,3,4]
L = []
for i in range(len(X)):
L.append(lambda x, i=i: X[i]**x) # capture i as a parameter
for f in L:
print(f(2))
Output:
1
4
9
16
You are expecting the value of i to be part of the function, not the name. That's not how function definitions work, either with def statements or lambda expressions.
I'd recommend defining a function maker, so that your expected function can close over the local value of i.
def make_function(i):
return lambda x: X[i]*x*x
Now i refers not to a global variable i, but to a local variable i in the function call. Every call to make_function creates a different local variable initialized with the value of the argument, and that variable is what your function will refer to when it is called.
for i in range(len(X)):
L.append(make_function(i))
Your lambda closes around the variable i. Despite what the for loops make it look like, there's actually only one variable i in your entire code, so when you call L[i](2) or L[0](2), you're using the current value of i. Let's look at your first example.
for i in range(len(X)):
print(L[i](2))
Here, we call L[i] with the enclosing value of i, so this is really basically
for i in range(len(X)):
print(X[i] ** 2)
On the other hand, in your second example
print(L[0](2))
The i value is the i from the final loop iteration above. To get around this, you need to explicitly capture the current value of i.
L.append(lambda x, i=i: X[i]**x)
This exploits one of Python's least intuitive features to explicitly capture a value in a lambda.

Use the result of the return of one function in another function [duplicate]

This question already has answers here:
How do I get a result (output) from a function? How can I use the result later?
(4 answers)
Closed 6 months ago.
I know there are several questions about this, I read but I could not understand. I'm trying to use the result of the return of one function in another:
def multiplication(a):
c = a*a
return c
def subtraction(c):
d = c - 2
return d
print(subtraction(c))
Output:
NameError: name 'c' is not defined
I know that there is a possibility of using global variables, but I have seen that this is not a good idea since the variables can change their value.
EDIT:
These two functions are just idiotic examples. I have two functions with words and I need to use the return of the first function in the second function. In case of this my idiotic example, I need the result of the first function (c) in the second function.
You are not calling your functions properly.
def multiplication(a):
c = a*a
return c
def subtraction(c):
d = c - 2
return d
# first store some value in a variable
a = 2
# then pass this variable to your multiplication function
# and store the return value
c = multiplication(a)
# then pass the returned value to your second function and print it
print(subtraction(c))
Does this makes things clearer?
def multiplication(a):
c = a*a
return c
def subtraction(c):
d = c - 2
return d
print(multiplication(5)) # 25
print(subtraction(5)) # 3
print(multiplication(subtraction(5))) # 9
print(subtraction(multiplication(5))) # 23
I think you're trying to do what's happing in the last print statement: first call the multiplication function, and then call the subtraction function on the result.
Note that the variable c in your multiplication function is an entirely different variable from the c in your subtraction function. So much so, that it may make things more clear to rename your variables, perhaps something like this:
def multiplication(a):
product = a * a
return product
def subtraction(a):
difference = a - 2
return difference
So why not use return value?
print(subtraction(multiplication(24)))
?
'c' is not declared outside the 'subtraction' function.
You need to give need to declare 'c' before printing.
Let's say you want 'c' to be 5, then:
c = 5
print(subtraction(c))
You have defined two functions which both return a number.
If you call subtraction(c) you will get the error you see, because there is no c.
If you define a c in scope of the print statmenet
c = 42
print(subtraction(c))
it will be ok.
Try thinking of it like this: each function takes a variable does things to it and returns a number.
e.g.
>>> multiplication(101)
10201
That this happened to be called c isnide the function isn't known outside the function (i.e scope).
You can save the number to a variable
>>> x = multiplication(101)
Then x remembers that value.
Or
>>> c = multiplication(101)
This is not the same c as you have inside the functions.
(And after the question edit):
Decide what value you want to call the first function with, for example 101:
>>> c = multiplication(101)
then use that return to call the next function:
>>>> subtraction(c)
Or just chain them togther:
subtraction( multiplication(101) )
To start the chain you will need to use a string, int or defined variable.
Otherwise you get name not defined errors.
Once a variable is used in a function it goes out of scope when the function ends.

In Python, why do function parameters maintain their value beetween function calls [duplicate]

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 5 years ago.
I'm fairly new at Python and i cannot wrap my head around the results im getting
Using the code below:
def func(a,b=set()):
res=list()
for i in a:
if i not in b:
res.append(i)
b|={i}
return res
print(func([1,1,2,2,3,4]))
print(func([1,1,2,2,3,4]))
I was getting output:
[1,2,3,4]
[]
I put "print(b)" above "res=list()" and got output:
set()
[1,2,3,4]
{1,2,3,4}
[]
What is going on? Shouldn't "b" be set to "set()" when i call the function? Im using Python 3.6
Have a look at the documentation for default parameters:
The default value is evaluated only once. This makes a difference when the default is a mutable object such as a list, dictionary, or instances of most classes.
When you define a function with a default parameter, the default value is only evaluated when the definition is first executed by the interpreter (the actual def statement). This is usually not a problem, except for when a mutable default value is used. That is to say, one that can be modified in place.
In your case, when you modify b in your function the first time you call it, it keeps that value the next time around. To avoid this, you can do like so:
def func(a,b=None):
if b is None:
b = set()
res=list()
for i in a:
if i not in b:
res.append(i)
b|={i}
return res
Now b will always have the default value you want.
In python functions are objects and arguments are evaluated and executed once. this is a nice explanation : http://effbot.org/zone/default-values.htm
in your example, it could be "fixed" by doing:
def func(a,b=None):
if b is None:
b = set()
res=list()
for i in a:
if i not in b:
res.append(i)
b|={i}
return res
In First function call b is empty
See here
In second function call b is already fill with element
So this condition not run if i not in b and return empty list
Try this
def func(a):
res=list()
b=set()
for i in a:
if i not in b:
res.append(i)
b|={i}
return res
print(func([1,1,2,2,3,4]))
print(func([1,1,2,2,3,4]))
Output
[1, 2, 3, 4]
[1, 2, 3, 4]

Function argument: original variable is modified even if passed by value [duplicate]

This question already has an answer here:
Strange behavior of lists in python [duplicate]
(1 answer)
Closed 8 years ago.
I have a function which basically uses the dijkstra's algorithm to find distances in a graph
def getDistances(q,start):
print(q)
q[start][0] = 0
queue = [start]
while len(queue):
m = []
mMin = queue[0]
m = q[mMin][2]
queue.remove(mMin)
for x in q[m][3]:
if q[x][1] == 0:
if not x in queue:
queue.append(x)
if q[x][0] > q[m][0] + 1:
q[x][0] = q[m][0] + 1
q[m][1] = 1
return [x[0] for x in q]
But it works perfectly. However, I have to call this function several times. and the "q" argument stays the same. Here's the invocation:
for ex in exits:
dist = getDistances(list(qt),ex)
distances.append([ex,dist])
"qt" itself is a list which contains a lot of different parameters. In this case I consider it not as important as the problem itself.
The problem
"qt" variable is modified if the "q" variable is modified inside the function. As you can see in invocation, i have tried to use list(qt) to pass the variable by value not by reference.
Thanks for any help in advance!
You have to create a deepcopy:
from copy import deepcopy
dist = getDistances(deepcopy(qt), ex)
You need to copy the elements in the list too. As for example: q[x][0] = q[m][0] + 1 modifies them. Do:
dist = getDistances(list(list(e) for e in qt), ex)

Why is a default value set for a Python 3 function overwritten when the function gets called multiple times? [duplicate]

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 8 years ago.
Can someone explain me why, when I call this function multiple times, L is never set to empty by default? But instead the result of any following call is L appended to all results of preceding calls?
The function separates the data into blocks of 7 days, starting with the last date ([::-1]),
calculates the mean of each 7 days and appends the result as a value to
a list. Ignores incomplete blocks
The default value for data is a list of dates in ordinal format.
def separate(data = [i for i in w][::-1],L = []):
print("separate has been called, data is %s and L is %s" % (data, L))
if len(data)<7:
return L
total = 0
dates = 0
for value in data[:7]:
if w[value] != "None":
total += float(w[value])
dates += 1
L.append(total / dates)
return separate(data[7:], L)
Taken from the the documentation:
The default value is evaluated only once. This makes a difference when the default is a mutable object such as a list, dictionary, or instances of most classes.
[...]
If you don’t want the default to be shared between subsequent calls, you can write the function like this instead:
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L

Categories