How to mutate a list with a function in python? - python

Here's a pseudocode I've written describing my problem:-
func(s):
#returns a value of s
x = a list of strings
print func(x)
print x #these two should give the SAME output
When I print the value of x in the end, I want it to be the one returned by func(x). Can I do something like this only by editing the function (and without setting x = func(x))

func(s):
s[:] = whatever after mutating
return s
x = a list of strings
print func(x)
print x
You don't actually need to return anything:
def func(s):
s[:] = [1,2,3]
x = [1,2]
print func(x)
print x # -> [1,2,3]
It all depends on what you are actually doing, appending or any direct mutation of the list will be reflected outside the function as you are actually changing the original object/list passed in. If you were doing something that created a new object and you wanted the changes reflected in the list passed in setting s[:] =.. will change the original list.

That's already how it behaves, the function can mutate the list
>>> l = ['a', 'b', 'c'] # your list of strings
>>> def add_something(x): x.append('d')
...
>>> add_something(l)
>>> l
['a', 'b', 'c', 'd']
Note however that you cannot mutate the original list in this manner
def modify(x):
x = ['something']
(The above will assign x but not the original list l)
If you want to place a new list in your list, you'll need something like:
def modify(x):
x[:] = ['something']

Related

Is it possible to use map on methods like List.append

I am wondering if you can use mapping like this:
x = ["a"]
y = ["b","c","d"]
map(x.append,y)
I would like x = ["a","b","c","d"], However this does not happen
map returns an iterator, so nothing happens as long as you don't iterate on it. You could make that happen by calling list on it:
x = ["a"]
y = ["b","c","dx"]
list(map(x.extend,y))
# [None, None, None]
print(x)
# ['a', 'b', 'c', 'd', 'x']
Note that, as extend expects an iterable (which str objects are), it will iterate on each string in y and append the characters, so you'll get 'd' and 'x' in the example above.
You probably meant to use append:
x = ["a"]
y = ["b","c","dx"]
list(map(x.append,y))
# [None, None, None]
print(x)
# ['a', 'b', 'c', 'dx']
But anyway, map is meant to yield the results of applying the function to the items of the iterable (here, the None returned by x.append()), and using it for the side effect of the function makes things rather unclear.
x = ["a"]
y = ["b","c","d"]
x.extend(y)
print (x)
output:
['a', 'b', 'c', 'd']
The extend() extends the list by adding all items of a list (passed as an argument) to the end.
The syntax of extend() method is:
list1.extend(list2)
Here, the elements of list2 are added to the end of list1.
Extend function takes a list as input as I know. With map() it would try to take an element, since map gives elements of an iterable to a function one by one and returns the result as a list again.
This should do
x.extend(y)
Any reason for doing this?
map is a worst choice in case of readability and execution speed.
x = ["a"]
y = ["b","c","d"]
x.extend(y)

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

Copy values from one list to another without altering the reference in python

In python objects such as lists are passed by reference. Assignment with the = operator assigns by reference. So this function:
def modify_list(A):
A = [1,2,3,4]
Takes a reference to list and labels it A, but then sets the local variable A to a new reference; the list passed by the calling scope is not modified.
test = []
modify_list(test)
print(test)
prints []
However I could do this:
def modify_list(A):
A += [1,2,3,4]
test = []
modify_list(test)
print(test)
Prints [1,2,3,4]
How can I assign a list passed by reference to contain the values of another list? What I am looking for is something functionally equivelant to the following, but simpler:
def modify_list(A):
list_values = [1,2,3,4]
for i in range(min(len(A), len(list_values))):
A[i] = list_values[i]
for i in range(len(list_values), len(A)):
del A[i]
for i in range(len(A), len(list_values)):
A += [list_values[i]]
And yes, I know that this is not a good way to do <whatever I want to do>, I am just asking out of curiosity not necessity.
You can do a slice assignment:
>>> def mod_list(A, new_A):
... A[:]=new_A
...
>>> liA=[1,2,3]
>>> new=[3,4,5,6,7]
>>> mod_list(liA, new)
>>> liA
[3, 4, 5, 6, 7]
The simplest solution is to use:
def modify_list(A):
A[::] = [1, 2, 3, 4]
To overwrite the contents of a list with another list (or an arbitrary iterable), you can use the slice-assignment syntax:
A = B = [1,2,3]
A[:] = [4,5,6,7]
print(A) # [4,5,6,7]
print(A is B) # True
Slice assignment is implemented on most of the mutable built-in types. The above assignment is essentially the same the following:
A.__setitem__(slice(None, None, None), [4,5,6,7])
So the same magic function (__setitem__) is called when a regular item assignment happens, only that the item index is now a slice object, which represents the item range to be overwritten. Based on this example you can even support slice assignment in your own types.

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)

when a python list iteration is and is not a reference

Could someone please offer a concise explanation for the difference between these two Python operations in terms of modifying the list?
demo = ["a", "b", "c"]
for d in demo:
d = ""
print demo
#output: ['a', 'b', 'c']
for c in range(len(demo)):
demo[c] = ""
print demo
#output: ['', '', '']
In other words, why doesn't the first iteration modify the list? Thanks!
The loop variable d is always a reference to an element of the iterable object. The question is not really a matter of when or when isn't it a reference. It is about the assignment operation that you are performing with the loop.
In the first example, you are rebinding the original reference of an element in the object, with another reference to an empty string. This means you don't actually do anything to the value. You just assign a new reference to the symbol.
In the second example, you are performing an indexing operation and assigning a new reference to the value at that index. demo remains the same reference, and you are replacing a value in the container.
The assignment is really the equivalent of: demo.__setitem__(c, "")
a = 'foo'
id(a) # 4313267976
a = 'bar'
id(a) # 4313268016
l = ['foo']
id(l) # 4328132552
l[0] = 'bar'
id(l) # 4328132552
Notice how in the first example, the object id has changed. It is a reference to a new object. In the second one, we index into the list and replace a value in the container, yet the list remains the same object.
In the first example, the variable d can be thought of a copy of the elements inside the list. When doing d = "", you're essentially modifying a copy of whatever's inside the list, which naturally won't change the list.
In the second example, by doing range(len(demo)) and indexing the elements inside the list, you're able to directly access and change the elements inside the list. Therefore, doing demo[c] would modify the list.
If you do want to directly modify a Python list from inside a loop, you could either make a copy out the list and operate on that, or, preferably, use a list comprehension.
So:
>>> demo = ["a", "b", "c"]
>>> test = ["" for item in demo]
>>> print test
["", "", ""]
>>> demo2 = [1, 5, 2, 4]
>>> test = [item for item in demo if item > 3]
>>> print test
[5, 4]
When you do d = <something> you are making the variable d refer to <something>. This way you can use d as if it was <something>. However, if you do d = <something else>, d now points to <something else> and no longer <something> (the = sign is used as the assignment operator). In the case of demo[c] = <something else>, you are assigning <something else> to the (c+1)th item in the list.
One thing to note, however, is that if the item d has self-modifying methods which you want to call, you can do
for d in demo:
d.<some method>()
since the list demo contains those objects (or references to the objects, I don't remember), and thus if those objects are modified, the list is modified too.

Categories