I was looking over my review paper and didn't understand the following coding.
def function_b(b_list, high_num):
c_list = [0, 0, 0, 0, 0, 0,0]
i = 0
for num in b_list:
if num > high_num:
c_list[i] = num
i = i + 1
b_list = c_list
def main():
b_list = [1,3, 6, 4, 1, 2, 8]
high_num = 4
function_b(b_list, high_num)
print(b_list)
main()
the result was [1, 3, 6, 4, 1, 2, 8]
Why did;t the b_list get updated with c_list?
As this answer explains:
If you pass a mutable object into a method, the method gets a
reference to that same object and you can mutate it to your heart's
delight, but if you rebind the reference in the method, the outer
scope will know nothing about it, and after you're done, the outer
reference will still point at the original object.
In other words, the line b_list = c_list only changes what b_list points to in the function. It doesn't change what b_list points to in main. So the solution is to modify the elements of b_list, rather than pointing b_list to a whole new list.
Another tip comes from this question. If you need both the index into the array, and the value at that index, you can use the enumerate function.
Putting it all together, the function could be written like this:
def function_b(b_list, high_num):
for i, num in enumerate(b_list): # use 'enumerate' to get both index and value
if num <= high_num: # check for small numbers in the list
b_list[i] = 0 # change the small numbers to 0
This is because inside function_b, b_list is just a reference that is being re-assigned. The actual list is not being modified.
A good solution would say return c_list towards the end of the function_b(..). main() can then say b_list = function_b(b_list, high_num)`.
In your particular case, you can do the following to get what you're looking for (although personally I tend to avoid doing this):
b_list[:] = c_list # Modified the actual contents of the list
# that b_list points at, instead of just
# making the local b_list point to whatever c_list
# is pointing at.
python function parameters are passed by reference and so it does not make a copy of the object when it gets passed in. The object can either be mutable(list) or immutable (str). You can perform work on the object the parameter points to but you can't really bind parameter to something else.
def function_b(b_list, high_num):
b_list = [] # this has no effect
b_list.append(10) # but you can modify it
While you can modify the function parameter since it s a list, the safest way is to rebind b_list in main() as a return value of the function_b mentioned in previous posts.
Related
While trying to implement an algorithm, I couldn't get python lists to mutate via a function. After reading up on the issue I was suggested by this StackOverflow answer to use [:] in order to mutate the array passed in the function argumemt.
However, as seen in the following code snippet, the issue still persists when trying to mutate the list l. I am expecting the output to be Before: [1,2,3,4]
After: [69, 69, 69, 69], but instead I get back the original value of l as shown below.
def mutate_list(a, b):
c = [69] * 4
a[:] = c[:2] # changed the elements, but array's still unchanged outside function
b[:] = c[2:]
if __name__ == '__main__':
l = [1, 2, 3, 4]
print("Before: {}" .format(l))
mutate_list(l[:2], l[2:])
print("After: {}" .format(l))
Output:
Before: [1, 2, 3, 4]
After : [1, 2, 3, 4]
Any insights into why this is happening?
The error is that you not pass actually the l but two slices of it. You should change it, for example:
def mutate_list(a):
c = [69] * 4
a[:2] = c[:2]
a[2:] = c[2:]
if __name__ == '__main__':
l = [1, 2, 3, 4]
print("Before: {}" .format(l))
mutate_list(l)
print("After: {}" .format(l))
its all about the scope, mutable concept is applicable on list but not to reference variable.
The variables a,b are local variables, hence the scope of the variable will be always function scope.
The operations which you have performed :
a[:]=c[:2]
b[:]=c[2:]
Note: a and b both are list now so you will get following output in the function:
[69,69],[69,69]
but if you use + operator which is use for adding operations then the out out will be like:
[69,69,69,69]
Now whatever I told you that will be a local scope, if you want that the list should be mutable across the program then you have to specify the scope of the list as global inside function and on that variable you can do changes. in this case you also dont need to pass any arguments:
def mutate_list():
global l # telling python to use this global variable in a local function
c = [69] * 4
l=c # assigning new values to actual list i.e l
Now before output will be [1,2,3,4]
and after will be [69,69,69,69]
As pointed out by others, the issue arose from the fact that the function parameters were slices of the original array and as such, the parameters were being passed by value (instead of being passed by reference).
According to #Selcuk 's suggestion, the correct way of doing such an operation would be to pass the original array along with its indices to the function and then perform any slicing inside the function.
NOTE: This concept comes in handy for (recursive) divide-and-conquer algorithms where subarrays must be mutated and combined to form the solution.
I wrote a small Programm in python but it don't work like expected.
Here's the code:
puzzle = [8, 7, 5, 4, 1, 2, 3, 0, 6]
def count(p):
p[0] += 1
return p
def main(p):
print(p)
l = count(p)
print(l)
print(p)
b1 = main(puzzle)
I expect that print(p) will be different from print(l), but the result of both is the same, it's the result that print(l) should have. But p did change also, however I would need it to be unchanged… Is this a special python behavior? Is there something I missed?
I also tried to change the variable names in the functions, but that didn't help.
I restarted the Compiler, but that didn't help either.
Is there a solution to store a function output and than call the function again without let the function change the given parameters?
So that l will be the result after the calculation and p will stay the value before?
Kind Regards,
Joh.
You are passing a List parameter. Parameter passing is Call-by-Object. Since a List is a mutable object in this situation it is similar to pass by reference and changes to your List object will persist. If you were passing an immutable, such as an Integer or String, it would be akin to pass by copy/value, and changes would not persist. E.g.:
def s2asdf(s):
s = "asdf"
s = "hello world"
s2asdf(s)
print s
... results in:
$ python example.py
hello world
The reason for this is because Python passes function parameters by reference. When you call the count function it allows the function to modify the list inside the function and the changes will be applied to the original object.
If you want to have the function not modify the list but instead return a different list, you will have to make a copy of the list either by passing a copy to the function or make a copy inside the function itself. There are many ways to copy a list in Python, but I like to use the list() function to do it.
This should fix your problem:
puzzle = [8, 7, 5, 4, 1, 2, 3, 0, 6]
def count(p):
new_list = list(p) # copy values of p to new_list
new_list[0] += 1
return new_list
def main(p):
print(p)
l = count(p)
print(l) # l is the new_list returned from count
print(p) # p stays the original value
b1 = main(puzzle)
>>> n = [1, 2, 3]
>>> for item in n:
... item *= 2
...
>>> print n
[1, 2, 3]
I expect the result of the above code to be [2, 4, 6], While obviously it's not.
Then I tried for i in range(n) as follows
>>> n = [1, 2, 3]
>>> for i in range(len(n)):
... n[i] *= 2
...
>>>
>>> n
[2, 4, 6]
This seems OK.
And my question is that, what's the essential difference between these two for loop method? What cause the unexpected result above?
If it helps, the first loop is equivalent to:
for i in range(len(n)):
item = n[i]
item *= 2
In other words, it first binds item to the i-th element of the list, and then rebinds it to a new object whose value is double that of the i-th element. It does not change any of the list's elements.
A good way to implement this loop is using a list comprehension:
n = [item * 2 for item in n]
You can't modify the object that represents the current iteration.
Well, actually, you can, but it won't change the object that is held in the list.
what's the essential difference between these two for loop method?
You iterate over objects in the list in the first example (and try to modify that said object directly - it doesn't change the list's element itself).
And you iterate over the list of integers in the second example (and actually modify the given list's element, so you modify the list content).
item is simply a local name. It is originally assigned by the for loop to point to the current element, but if you reassign it to point to something else, that has no effect on the original.
But if you use an index to reference an element in the original list, you can mutate that list to contain different values.
There's no assignment in for item in lst. You're operating on the object itself, which is immutable, so it just creates a new object for you when you do the *= call, assigns it to item, then throws it away on the next iteration of the loop.
When you do for i in range(len(lst)) you're assigning the new object to the ith element of lst.
I don't understand this behaviour:
def getvariable(v):
v += 1
def getlist(l):
l.append(8)
myvariable = 1
mylist = [5, 6, 7]
print myvariable, mylist
getvariable(myvariable)
getlist(mylist)
print myvariable, mylist
Output:
1 [5, 6, 7]
1 [5, 6, 7, 8]
Why list changed, but variable doesn't?
How can I change variable in function?
Many people say about passing by value, by reference, by object reference, so I am a bit confused and don't know how it is really.
In python integers are immutable. v += 1 only binds a new integer value to the name v, which is local in your function. It does not modify the integer in place.
Lists in python are mutable. You pass a list (by reference, as always in python), and the function changes it in place. That's why the change is "seen" externally to the function.
There is no such thing as "passing by value" in python.
What you probably want to do is return v+1 from your function, not to modify the value bound to the name v.
Because, lists are mutable but integers are immutable.
Read more about it here: http://docs.python.org/2/reference/datamodel.html#objects-values-and-types
folks, i got a question about passing mutable object to a function
with the following code, I was expecting the output to be [0,0,0], while the output is [0,1,2,3]
does it mean the argument is actually copied and then send to the inside of the function?
def lala(a):
n = [0, 0 , 0]
a = n
a = [0,1,2,3]
lala(a)
print a
if i want to fulfill the above task inside the function, how shall i write it elegantly?
thanks very much!
Python makes more sense if you think of attaching name tags to objects, rather than stuffing objects into named boxes.
def lala(a):
n = [0, 0 , 0]
a = n
Here's what's happening.
You're receiving a parameter (a list in this case) and giving it the name a.
You're creating a new list and giving it the name n.
You are giving the list you named n the additional name a.
Both names, a and n, are local to the lala function, so they "expire" when the function ends.
The list you created in the function now has no names, and Python discards it.
In other words, a is not a box into which you can put a new list. It is a name you gave a list you received. Later, you reassign that name to the list you have also named n.
Others have suggested a slice assignment, a[:] = n, which uses a reference to the items in the list rather than the list itself. When you do this, the name a still points to the same list; its contents have just been replaced. Since it is the same list that was passed into the function, any names by which it is known outside the function will "see" the new contents.
kindall provided a great explanation in my opinion, but my preferred method for doing something like this is to have the function return the change instead of messing with references.
def lala():
n = [0, 0, 0]
return n
a = [0, 1, 2, 3]
a = lala()
print a # prints [0, 0, 0]
you can use a[:] = n inside the function. this is called a slice assignment
python does not have a call-by-reference feature. There's no general way to do this.
If you know your argument is going to be a list, and you want it to take a different value, you can write it like so:
def lala(a):
a[:] = [0,0,0]
That's because a function makes new label "a" for its scope. Then this "a" overshadows the "a" you defined outside the function. So the new label a is assigned to new object [0,0,0,0]
If you would write:
def lala(a):
a.pop()
a = [0,1,2,3,4]
lala(a)
print(a)
You would see that a = [0,1,2,3] because pop() actually change the object which label 'a' points to. (while assignment just change to what object given label points to)
Note the difference between your function, and this one:
def lala(a):
a[0] = 7
a = [0,1,2,3]
lala(a)
print a # prints [7, 1, 2, 3]