How do I properly pass an argument to a function - python

I'm from a C++ background so this problem seems a little absurd to me:
Let's suppose I have a function:
def scale(data, factor):
for val in data:
val *= factor
This doesn't work as intended, if I pass a list, it changes nothing, but
def scale(data, factor):
for index, val in enumerate(data):
data[index] *= factor
and lst = [val * factor for val in lst] works properly.
How does Python handle argument passing? How do I know if the actual reference, or alias is passed?

if you want to mutate the list, you need to reference the elements. This version uses map (it could be written using list comprehensions)
def scale(data, factor):
return map(lambda x : x*factor, data)
a lambda function is an anonymous function.
>>> (lambda x : x + 1) (5)
6
The x takes the place of the variable in this case 5 + 1
So in this case, we traverse the list applying the function f(x) -> x * factor to every element of the list. The original list is not mutated, but instead we return a new version.

In python basic data types are passed by value - for example int, str, bool etc are passed by value
Derived data types like classes, enum, list, dict are passed by reference.
In your example, the problem is how you use the for loop - not the function argument. If you do:
for val in lst:
val += 1
The values inside lst won't get updated because the val is not the same as lst[0], lst[1] and so on IF val is of the basic data types. So, even here, the val is copied by value.
Second, In your example with enumerate:
But when you loop over the enumerated list, you are using data[index] - which modifies the element in the actual list.
And finally, In your example with the generator:
lst = [val * factor for val in lst] - here the generator loops over every element and creates a new list which is again stored in lst. This is something like a = a + 2 but extended to lists.

This behaviour is so because the basic data types are passed by value and the derived data types like lists are passed by reference consider this
>>> x = 24
>>> x + 1
25
>>> x
24
but on the otherhand with a list
>>> y = [1, 2, 3, 4]
>>> y.remove(2)
>>> y
[1,3,4]
so you should always be careful to reassign values back when performing operations on them in the case of the basic data ypes and also be careful with datatypes that are passed by reference because you could accidentally modify a variable without knowing

Related

How to update list with function (with return value) in python

I am implementing few list methods manually like append(), insert(), etc. I was trying to add element at the end of list (like append method). This the working code i am using:
arr = [4,5,6]
def push(x, item):
x += [item]
return x
push(arr,7)
print(arr) #Output: [4,5,6,7]
But when I am implementing same code with little difference. I am getting different output.
arr = [4,5,6]
def push(x, item):
x = x + [item]
return x
push(arr,7)
print(arr) #Output: [4,5,6]
And I am facing same for insert method. Here is code for insert method:
arr = [4,5,7,8]
def insert(x, index, item):
x = x[:index] + [item] + x[index:]
return x
insert(arr,2,6)
print(arr) #Output: [4,5,7,8]
I know I can store return value to the list by arr=insert(arr,2,6) but I want an alternative solution, that list automatically gets update after calling function like in my first code sample.
Edit 1:
I think x[index:index] = [item] is better solution for the problem.
x += [item] and x = x + [item] are not a little difference. In the first case, you are asking to make a change to the list referenced by x; this is why the result reflects the change. In the second, you are asking to have x reference a new list, the one made by combining x's original value and [item]. Note that this does not change x, which is why your result is unchanged.
Also note that your return statements are irrelevant, since the values being returned are ignored.
In your first example you mutated(a.k.a changed) the list object referred to by x. When Python sees x += [item] it translates it to:
x.__iadd__([item])
As you can see, we are mutating the list object referred to by x by calling it's overloaded in-place operator function __iadd__. As already said, __iadd__() mutates the existing list object:
>>> lst = [1, 2]
>>> lst.__iadd__([3])
[1, 2, 3]
>>> lst
[1, 2, 3]
>>>
In your second example, you asked Python to assign x to a new reference. The referenced now referrer to a new list object made by combining (not mutating) the x and [item] lists. Thus, x was never changed.
When Python sees x = x + [item] it can be translated to:
x = x.__add__([item])
The __add__ function of lists does not mutate the existing list object. Rather, it returns a new-list made by combing the value of the existing list and the argument passed into __add__():
>>> lst = [1, 2]
>>> lst.__add__([3]) # lst is not changed. A new list is returned.
[1, 2, 3]
>>>
You need to return the the result of the version of push to the arr list. The same goes for insert.
You can assign to a slice of the list to implement your insert w/o using list.insert:
def insert(x, index, item):
x[:] = x[:index] + [item] + x[index:]
this replaces the contents of the object referenced by x with the new list. No need to then return it since it is performed in-place.
The problem is that you haven't captured the result you return. Some operations (such as +=) modify the original list in place; others (such as x = x + item) evaluate a new list and reassign the local variable x.
In particular, note that x is not bound to arr; x is merely a local variable. To get the returned value into arr, you have to assign it:
arr = push(arr, 7)
or
arr = insert(arr, 2, 6)
class DerivedList(list):
def insertAtLastLocation(self,obj):
self.__iadd__([obj])
parameter=[1,1,1]
lst=DerivedList(parameter)
print(lst) #output[1,1,1]
lst.insertAtLastLocation(5)
print(lst) #output[1,1,1,5]
lst.insertAtLastLocation(6)
print(lst) #output[1,1,1,5,6]
You can use this code to add one element at last position of list
class DerivedList(list):
def insertAtLastLocation(self,*obj):
self.__iadd__([*obj])
parameter=[1,1,1]
lst=DerivedList(parameter)
print(lst) #output[1,1,1]
lst.insertAtLastLocation(5)
print(lst) #output[1,1,1,5]
lst.insertAtLastLocation(6,7)
print(lst) #output[1,1,1,5,6,7]
lst.insertAtLastLocation(6,7,8,9,10)
print(lst) #output[1,1,1,5,6,7,8,9,10]
This code can add multiple items at last location

How not to change outer values?

I am new to Python, therefore my question will look like pretty foolish.
I try to make some program that makes two-dimensional array. One function puts items to list and returns an array. Then, second function put results of the first function and puts it in outer list.
My program looks like this:
def get_matrix():
d = some_dict
matrix = list()
while len(matrix)<3:
row = get_row(d)
matrix.append(row)
return matrix
def get_row(dict):
array = list()
for t in range(3):
a = dict.popitem()
array.append(a)
return array
some_dict = dict()
for x in range(9):
some_dict[x] = "a"+str(x)
print(some_dict)
print(get_matrix())
It works well. But what if I want not to change list d in outer function but just do it so:
def get_matrix():
d = some_dict
matrix = list()
while len(matrix)<3:
row = get_row(d)
matrix.append(row)
for x in row:
d.pop(x)
return matrix
In other words, I want to keep the whole dict in outer function.
Actually I want know why the outer values change if we change only the dict given by arguments of inner function?
You are popping items of the dict in the inner function. Since you are passing a handle to a mutable object (dict) to your get_row() function, the popping will effect the dict in the outer get_matrix as well.
Note that a dict as a complex object behaves differently in that regard than an immutable parameters (int, float, str, etc.). Their values remain unchanged.
You could pass a copy of your dict
row = get_row(copy.deepcopy(d))
as explained here: Understanding dict.copy() - shallow or deep?.
Btw, you can access the values in your dict without popping them
def get_row(dict):
...
for t in range(3):
...
# a = dict.popitem()
a = dict[t]
which will also leave the dict unharmed.

Reverse list using map/reduce

I am learning concepts of functional programming and trying out problem exercises.
An exercise,
Reverse a list using map/reduce.
My solution:
lists = [ 1, 2, 3, 5 ,6]
def tree_reverse(lists):
return reduce(lambda a, x: a + [a.insert(0,x)], lists, [])
print tree_reverse(lists)
Output:
[6, 5, 3, 2, 1, None, None, None, None, None]
I don't understand why there are Nones equal to number of elements in list.
EDIT: Extending question for case of nested lists.
lists = [ 1 ,2 ,3 , [5,6,7], 8, [9, 0, 1 ,4 ]]
At first, i don't know Python good enough, but as you marked the question as "functional-programming" and you stated you want to learn in general about functional programming i want to give some general explanation of your problem.
At first, you should rethink what the map and reduce operation do and what they expect. Let's start with the map function.
A map function for a list (in functional programming you don't have only one map, you have one map for every generic type) executes a function on every single argument. That function can operate on that single argument and transform it. The result of this transformation is turned into a new list. One important note is that the function you pass to map only ever sees one element, so you cannot use map itself to transform a list as a whole. You only can transform every single element of a list. But changing the order needs the idea of having at least two elements of a list, and a map only ever gets a single element. This is the reason why the reverse logic cannot be in map and has to be part of the reduce operation.
The reduce function on the other hand expects a function that takes two things of the same type, and returns a single item of the same type. For example the reduce function could take the "+" function. "+" is a function that takes two int and returns a new int. The signature is basically int + int = int. The essence of reduce is to take two things and turn it into a single new item. If you for example had something more complex like a class "Foo", then the function you have to provide to reduce need to have the signature Foo * Foo -> Foo. Meaning take a Foo as first and second argument, and return a single new Foo.
It is also important to note how reduce will work with that function. Let's assume you have the List [1,2,3,4,5] on top let's assume you have an accumulator function that takes two int that just adds them together. What you can think of what will happen is the following.
The reduce function takes the first two arguments of your list (1 and 2 in this case)
Passes those to the accumulator function (1 + 2 = 3)
And replaces the two arguments in the list with the result. [3,3,4,5]
Go back to 1) and repeat that process until your List only have a single item
Return that single item
So what you can imagine what happens is the following
[1,2,3,4,5] -> 1 + 2 = 3
[3,3,4,5] -> 3 + 3 = 6
[6,4,5] -> 6 + 4 = 10
[10,5] -> 10 + 5 = 15
[15] -> Return just "15". Not a "[15]"
Actually the process internal usually works a little bit different. But this is the process you can imagine what happens. It is important to note that you never modify a list. You always create new lists in that process. Another important note. At the end of the reduce function you have a list of an single item. But the reduce function does not return the whole list. It returns just the single element. So you get 15 an int as a result. You don't get a list with a single item containing 15. Reduce will just return that single element. Whatever it is. One other way to think about it. You always get exactly the type back of your accumulator function. If you pass a accumulator function that takes two int adds them and returns a new int. Your reduce function will also return an int. If you pass an accumulator function that takes two Foo classes and returns a new Foo. Your reduce function will also return a Foo as its result. The return type of reduce is always the same as the types of your accumulator function.
Now let's take all those pieces and put them together. The goal is to reverse a list. The first important thing is. Your result type will be a list. That also means that the function you pass into reduce also have to return a list. But as the input are always the same as the output. You now have to provide a accumulator function that takes two lists, and returns a single new list.
But now let's step back. What happens if you use your list "[1,2,3,4,5]" directly as the input to reduce? The short answer is, it will not work. reduce will take two argument of your list. But what you have is a list of int. But your accumulator function expects two lists not two int. To solve that problem, what you now can do is try to convert every single element of the list into its own list. So how do we transform every single element of a list? Right! With map! So what you have to do is the following. You first map your list into a list of lists.
[1,2,3,4,5] -> [[1],[2],[3],[4],[5]]
Now your reduce function gets the first two elements of your first list. It means you now have an accumulator function that gets [1] and [2] as its argument. Two separate lists. But your accumulator function has to return a single new list. If unclear, just remind what the accumulator function takes and return.
int, int -> int
float,float -> float
Foo,Foo -> Foo
list,list -> list
So what you now have to do is to combine those two lists into a single new list. Or in other words you have to append/concat those two lists. I don't knew exactly how you concat two lists in Python let's assume here the operation would be "++". So if you concat just the first argument and the second argument, you will not get what you want.
[[1],[2],[3],[4],[5]] -> [1] ++ [2] = [1,2]
[[1,2],[3],[4],[5]] -> [1,2] ++ [3] = [1,2,3]
[[1,2,3],[4],[5]] -> [1,2,3] ++ [4] = [1,2,3,4]
[[1,2,3,4],[5]] -> [1,2,3,4] ++ [5] = [1,2,3,4,5]
[[1,2,3,4,5]] -> Return first element [1,2,3,4,5]
So what you have to do is to concat the second argument with the first one.
[[1],[2],[3],[4],[5]] -> [2] ++ [1] = [2,1]
[[2,1],[3],[4],[5]] -> [3] ++ [2,1] = [3,2,1]
[[3,2,1],[4],[5]] -> [4] ++ [3,2,1] = [4,3,2,1]
[[4,3,2,1],[5]] -> [5] ++ [4,3,2,1] = [5,4,3,2,1]
[[5,4,3,2,1]] -> Return first element [5,4,3,2,1]
And now what you get is your reversed list. So what you have todo
map every element to a list. So you get a list of list
use reduce to concat every list of list into a new list in reversed order.
For example, in F# the whole code would look like this.
let list = [1;2;3;4;5]
let reversed =
list
|> List.map (fun x -> [x]) // map every element to a list
|> List.reduce (fun xs ys -> List.append ys xs) // Note ys ++ xs - reversed order for appending
printfn "%A" reversed
// prints: [5;4;3;2;1]
I think you should be able to translate this to Python. As a further notice. Doing "map" and then a "concat" operation (without reversing) is also named "bind" in functional programming.
Your reduce operation is appending a None to the end of the list a because a.insert(0,x) will return None.
So you are modifying the list a in-place, but appending a None value at the same time.
If you rewrite your reduce operation to use a for loop, it'd look like:
def reverse_list(lists):
r = []
for x in lists:
val = r.insert(0,x)
r = r + [val] # val (the return value of r.insert) is None
return r
In case of nested list. I am posting answer for reference.
lists = [ 1 ,2 ,3 , [5,6,7], 8, [9, 0, 1 ,4 ]]
def tree_reverse(lists):
return reduce(lambda a, x: ( map(tree_reverse, [x]) if isinstance(x,list) else [x] ) + a , lists , [] )
print tree_reverse(lists)
Output :
[[4, 1, 0, 9], 8, [7, 6, 5], 3, 2, 1]
Functional programming languages will often use recursion to implement iteration.
A recursive tree_reverse function might look like
def tree_reverse(lists):
return tree_reverse(lists[1:]) + [lists[0]] if lists else []
If you look at Haskell it might be implemented like this
reverse' :: [a] -> [a]
reverse' [] = []
reverse' (x:xs) = reverse' xs ++ [x]
Where the x is the head of the list (first element) and xs is the tail (the list minus the head) and you can see that reverse' is called recursively with those elements reversed and the reversed list is built up iteratively.

Python - external variable being changed by function

I am trying to write a function that squares each value in a list, returning a new list with individual values squared. I pass a list to it and it changes the original list. I'd like it to not make any changes to the original list, so that I can use it in other functions.
def squareset(c):
d=c
count = len(c)
print(c)
for i in range(0,(count),1):
d[i]=d[i]**2
return d
test = [1,2,3]
print(squareset(test))
print(test)
I don't have this problem with functions operating on simple variables of type int or float.
I added the d=c line trying to prevent the change to the list test, but it makes no difference. print(test) is producing the result [1,4,9] instead of [1,2,3]. Why is this happening and how can I fix this?
Doing d=c simply makes the parameter d point to the same object that c is pointing to. Hence, every change made to d is made to the same object that c points to.
If you want to avoid changing c you'll have to either send a copy of the object, or make a copy of the object inside the function and use this copy.
For example, do:
d = [i for i in c]
or:
d = c[:]
instead of:
d = c
Assigning the list to another variable doesn't copy the list. To copy, just
def squareset(c):
d=c[:]
...
While the other answers provided are correct I would suggest using list comprehension to square your list.
In [4]:
test = [1,2,3]
results = [elm**2 for elm in test]
print test
print results
[1, 2, 3]
[1, 4, 9]
If you wanted a function:
def squareList(lst):
return [elm**2 for elm in lst]
Try this:
def square(var):
return [x*x for x in var]
x = [1,2,3]
z = square(x)

How to uniformly handle A LIST or A LIST OF LIST (multi-dimensional list)

Without any heavy libraries such as numpy, I want to uniformly handle a single list or multi-dimensional list in my code. For example, the function sum_up(list_or_matrix) should
return 6 for argument [1, 2, 3] and return 9 for [[1, 2, 3], [1, 2, 0]].
My question is:
1. Can I code in a way without explicitly detecting the dimension of my input such as by isinstance(arg[0], (tuple, list))?
2. If I have to do so, is there any elegant way of detecting the dimension of a list (of list of list ...), e.g. recursively?
As many users suggested you can always use dict instead of list for any-dimensinal collection. Dictionaries are accepting tuples as arguments as they are hashable. So you can easy fill-up your collection like
>>> m = {}
>>> m[1] = 1
>>> m[1,2] = 12
>>> m[1,2,"three",4.5] = 12345
>>> sum(m.values()) #better use m.itervalues() in python 2.*
12358
You can solve this problem using recursion, like this:
#!/usr/bin/env python
def sum(seq_or_elem):
if hasattr(seq_or_elem, '__iter__'):
# We were passed a sequence so iterate over it, summing the elements.
total = 0
for i in seq_or_elem:
total += sum(i)
return total
else:
# We were passed an atomic element, the sum is identical to the passed value.
return seq_or_elem
Test:
>>> print(sum([1, 2, [3, [4]], [], 5]))
15
Well I dont see a way if you are planning to use a single function to sum up your list like sum_up(list_or_matrix).
If you are having a list of lists I would only imagine you need to loop through the list to find out if its a 1-D or a 2-D list. Anyway whats wrong in looping?
def sum_up(matrix):
2DMatrix = False
for item in matrix:
if type(item) == list:
2DMatrix = True
if(2DMatrix):
//sum 2d matrix
else:
//sum 1d matrix
A simple way to sum up a matrix is as follow:
def sum_up(matrix):
if isinstance(matrix, (tuple, list)):
return sum(matrix)
else:
return sum((sum(x) for x in matrix))
The 'else' branch uses list comprehension, a powerful and quick tool.
You could sum recursively until you have a scalar value:
def flatten(x):
if isinstance(x, list):
return sum(map(flatten, x))
return x
Note: you can use collections.Iterable (or another base class) instead of list, depending on what you want to flatten.

Categories