Passing variable and list to function in Python - python

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

Related

When is it best to use a python map function with a user defined function with parentheses or without parentheses

Sorry if this sound like a dumb question, I really do not understand why am i getting values back and why the complier is not screaming error, first the below code the function add_to_five receive an (x) in the (parentheses) and return the value of x + 5; however when called in the map function i only pass in add_to_five without parentheses and provided a iterable sequence of l for the second arg and the new_value is [6, 7, 8, 9, 10]
how is x getting assigned, when called in the the map the add_to_five clearly does not get the parentheses explicitly assigned and the value of (x) is not provided to the function, however some how l is been replaced into the parentheses of add_to_five that why the result is [6, 7, 8, 9, 10].
when i take away (x +) in the return and just return only 5 though still left an (x) in the parentheses of add_to_five the new_value become [5,5,5,5,5] and i do not get an error when add_to_five referenced in the map function.
why does add_to_five work in the map function with out parentheses and also worked when i returned just (5) and still leave the (x) in the parentheses with out binding (x) to anything or value (5).
when is it best to use a function with parentheses as i know when invoked (type of add_to_five(5)) it return (int) as object type and when invoked (type of add_to_five) it returns function and random memory address.
I know lambda is a way to work around but not looking for quick solution thank you.
def add_to_five(x):
return x + 5
l = [1, 2, 3, 4, 5]
new_value = list(map(add_to_five, l))
print(new_value)
Think about functions in Python as simply objects. Objects can be passed to functions and also can be returned from functions. This is actually functional-style programming.
In your map function, you pass in a function and a list or any other object/collection that can be iterated.
Why you don't include the parantheses is because it is not a function call.
You'll always hear variables store data or references to data. But here the variable actually holds a reference to code.
When you create a function:
def func(arg1, arg2):
...
func is a function object. It is a variable that holds a reference to the code that is contained within the function.
If I do:
x = func
Here I have not used parantheses even though func is a function.
I can now do this:
x(1, 2)
It is because I assigned x as func. Calling x is equivalent to calling func.
This is a simplified form of how the map function works:
def map(function_to_call, iterable):
result = []
for element in iterable:
r = function_to_call(element)
result.append(r)
return result
Now I can go ahead and call map like this:
def square(x):
return x * x
map(square, [1, 2, 3, 4])
Map function does not return a list, it returns a map object that can be converted to a list. Here I have simplified it to prevent distraction.
So map takes in a function and a sequence, iterates over that sequence and calls the passed function on each and every element and creates a new sequence that contains the results. Map only takes a reference to the function so that it can call that function later during execution.
add_to_five similarly is a function object. It is passed to map so that map can call it later multiple times for each element.
This is advanced, so the confusion is common.
Hope this helps, let me know if something is not clear.
Maybe this helps explaining it:
Map is a function by it self, like any other function you provide the parameters for the function in (parentheses) and seperated by comma. Map works the same, only difference is that you do not insert an integer or string but a function, see here:
def add_to_five(x):
return x + 5
l = [1, 2, 3, 4, 5]
def map_function(a_function, lst):
for i, element in enumerate(lst):
lst[i] = a_function(element)
return lst
print(map_function(add_to_five, l))
#[6, 7, 8, 9, 10]

Why does accessing list items via index in a function work when changing its value, but the iterator variable way doesn't? [duplicate]

This question already has answers here:
How do I operate on the actual object, not a copy, in a python for loop?
(3 answers)
Closed 2 years ago.
I am trying to increment the elements of a list by passing it into a increment() function that I have defined.
I have tried two ways to do this.
Accessing using the index.
# List passed to a function
def increment(LIST):
for i in range(len(LIST)):
LIST[i] += 1
return LIST
li = [1, 2, 3, 4]
li = increment(li)
print(li)
This outputs the desired result: [2, 3, 4, 5]
Accessing using iterator variables.
# List passed to a function
def increment(LIST):
for item in LIST:
item += 1
return LIST
li = [1, 2, 3, 4]
li = increment(li)
print(li)
This outputs: [1, 2, 3, 4]
I wish to know the reason behind this difference.
Python's in-place operators can be confusing. The "in-place" refers to the current binding of the object, not necessarily the object itself. Whether the object mutates itself or creates a new object for the in-place binding, depends on its own implementation.
If the object implements __iadd__, then the object performs the operation and returns a value. Python binds that value to the current variable. That's the "in-place" part. A mutable object may return itself whereas an immutable object returns a different object entirely. If the object doesn't implement __iadd__, python falls back to several other operators, but the result is the same. Whatever the object chooses to return is bound to the current variable.
In this bit of code
for item in LIST:
item += 1
a value of the list is bound to a variable called "item" on each iteration. It is still also bound to the list. The inplace add rebinds item, but doesn't do anything to the list. If this was an object that mutated itself with iadd, its still bound to the list and you'll see the mutated value. But python integers are immmutable. item was rebound to the new integer, but the original int is still bound to the list.
Which way any given object works, you kinda just have to know. Immutables like integers and mutables like lists are pretty straight forward. Packages that rely heavily on fancy meta-coding like pandas are all over the map.
The reasoning behind this is because integers are immutable in python. You are essentially creating a new integer when performing the operation item +=1
This post has more information on the topic
If you wished to update the list, you would need to create a new list or update the list entry.
def increment(LIST):
result = []
for item in LIST:
result.append(item+1)
return result
li = [1, 2, 3, 4]
li = increment(li)
print(li)

List is unchanged ever after element is changed

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.

Copy a variable in python (jupyter) / Use different functions with same variables

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)

Python Variable Scope (passing by reference or copy?)

Why does the variable L gets manipulated in the sorting(L) function call? In other languages, a copy of L would be passed through to sorting() as a copy so that any changes to x would not change the original variable?
def sorting(x):
A = x #Passed by reference?
A.sort()
def testScope():
L = [5,4,3,2,1]
sorting(L) #Passed by reference?
return L
>>> print testScope()
>>> [1, 2, 3, 4, 5]
Long story short: Python uses pass-by-value, but the things that are passed by value are references. The actual objects have 0 to infinity references pointing at them, and for purposes of mutating that object, it doesn't matter who you are and how you got a reference to the object.
Going through your example step by step:
L = [...] creates a list object somewhere in memory, the local variable L stores a reference to that object.
sorting (strictly speaking, the callable object pointed to be the global name sorting) gets called with a copy of the reference stored by L, and stores it in a local called x.
The method sort of the object pointed to by the reference contained in x is invoked. It gets a reference to the object (in the self parameter) as well. It somehow mutates that object (the object, not some reference to the object, which is merely more than a memory address).
Now, since references were copied, but not the object the references point to, all the other references we discussed still point to the same object. The one object that was modified "in-place".
testScope then returns another reference to that list object.
print uses it to request a string representation (calls the __str__ method) and outputs it. Since it's still the same object, of course it's printing the sorted list.
So whenever you pass an object anywhere, you share it with whoever recives it. Functions can (but usually won't) mutate the objects (pointed to by the references) they are passed, from calling mutating methods to assigning members. Note though that assigning a member is different from assigning a plain ol' name - which merely means mutating your local scope, not any of the caller's objects. So you can't mutate the caller's locals (this is why it's not pass-by-reference).
Further reading: A discussion on effbot.org why it's not pass-by-reference and not what most people would call pass-by-value.
Python has the concept of Mutable and Immutable objects. An object like a string or integer is immutable - every change you make creates a new string or integer.
Lists are mutable and can be manipulated in place. See below.
a = [1, 2, 3]
b = [1, 2, 3]
c = a
print a is b, a is c
# False True
print a, b, c
# [1, 2, 3] [1, 2, 3] [1, 2, 3]
a.reverse()
print a, b, c
# [3, 2, 1] [1, 2, 3] [3, 2, 1]
print a is b, a is c
# False True
Note how c was reversed, because c "is" a. There are many ways to copy a list to a new object in memory. An easy method is to slice: c = a[:]
It's specifically mentioned in the documentation the .sort() function mutates the collection. If you want to iterate over a sorted collection use sorted(L) instead. This provides a generator instead of just sorting the list.
a = 1
b = a
a = 2
print b
References are not the same as separate objects.
.sort() also mutates the collection.

Categories

Resources