Unwanted list changing through functioncall - python

The general project is to re-build an enigma machine. However, the problem is this:
I have a list and I want to rearrange it in a way that the first element is appended to the same list and then the first element is deleted.
Basicly, I want this:
l = [1, 2, 3, 4]
l_new = [2, 3, 4, 1]
for this purpose I constructed the function
def switch_up(list):
list.append(list[0])
del list[0]
return list
The problem: When calling this function with a list, it does not only return the changed list, but changes the original list given as the argument.
The full code looks like this:
def switch_up(list):
list.append(list[0])
del list[0]
return list
my_list = [1, 2, 3, 4]
my_list2 = switch_up(my_list)
print(my_list)
print(my_list2)
My expected/desired output would be:
[1, 2, 3, 4]
[2, 3, 4, 1]
The output I get is:
[2, 3, 4, 1]
[2, 3, 4, 1]

You are altering the list passed into the function. That isn't a copy of the list it's the same list. You should just make a copy and return it with something like:
def switch_up(l):
# list slices will make shallow copy
return l[1:] + l[:1]
my_list = [1, 2, 3, 4]
my_list2 = switch_up(my_list)
print(my_list)
# [1, 2, 3, 4]
print(my_list2)
# [2, 3, 4, 1]

Lists are mutable in python and therefore it is assumed, that you want to do operations on the original list. If you do not want that you have to manually copy your list for example with list.copy. For more information on copy please see this stackoverflow question
All you created is a "pointer" called my_list2 to the same value as my_list if you would for example iclude my_list2[1] = 'a' the result would be:
my_list = [2, 'a', 4, 1]
my_list2 = [2, 'a', 4, 1]
And btw you should never name your variables the same as inbuilt functions, like list

Related

Recursion with list but I got some [..]

I tried to create a problem like [0,1,2,[0,1,2,[0,1,2,[0,1,2] ] ]... by using recursion it but it got some bugs that I don't know where it came from.
It creates an output like this:
[1, 2, 3, 4, 5, [...]]
So let's make an example x = [1,2,3,4,5] and len_ = len(x) for the recursion. The code will be like this:
list_ = [1,2,3,4,5]
n = len(list_)
dull = []
def BlastedField3(n):
global list_
list_ += [list_]
if n == 1:
return list_
if n > 1:
return list_
return BlastedFild(n-1)
print(BlastedField3(n))
So I was imagining that list_ += [list_] was something like per each recursion of the function the list_ += [list_] working too.But! it works but the bug [...] is coming up next to it.
Is there a way to solve this without this bug [...] or just try a loop?
The problem of creating a self-containing list can be solved by not using references but shallow copies and by not reusing global variables (whenever you feel the need for those, look at your design long and hard).
base = [1,2,3,4,5]
def blasted(n):
if n <= 1:
return base[:] # shallow copy to never accidentally expose your base
return base + [blasted(n-1)]
>>> blasted(1)
[1, 2, 3, 4, 5]
>>> blasted(2)
[1, 2, 3, 4, 5, [1, 2, 3, 4, 5]]
>>> blasted(3)
[1, 2, 3, 4, 5, [1, 2, 3, 4, 5, [1, 2, 3, 4, 5]]]
Actually the use of shallow copies is not strictly necessary as the code never mutates the base list. But I would still recommend it because if someone were to mutate any result of the blast, base might be affected.

My_list.extend([My_list.pop(), my_list.pop])

My code:
my_list = [1,2,3,4,5]
my_list.extend([my_list.pop(), my_list.pop(), my_list.pop()])
So when I print it the result is
[1, 2, 5, 4, 3]
so why this Skew the last three Elements
Python's order of execution is inside-out and left-to-right.
So here python fill first pop() three elements from my_list and create a list from those, left to right -> [5, 4, 3] then it will execute extend appending everything front-to-back.
Essentially what your code does is:
my_list = [1, 2, 3, 4, 5]
second = []
second.append(my_list.pop())
second.append(my_list.pop())
second.append(my_list.pop())
my_list.extend(second)

Appending variable that is modified in a for loop doesn't work as expected [duplicate]

This question already has answers here:
List on python appending always the same value [duplicate]
(5 answers)
Closed 4 years ago.
I have this code:
lst = []
given = [1, 2, 3, 4, 5]
result = []
for item in given:
lst.append(item)
print(lst)
result.append(lst)
print(result)
My expected result is [[1], [1, 2], [1, 2, 3], ...], but displayed result is [[1, 2, 3, 4, 5], ...] with 12345 repeated 5 times. What is wrong?
lst printed is as expected, which is [1] for the first loop, [1, 2] for the second loop, and so on.
Python doesn't create copy of lst every time when you append it to result, it just inserts reference. As a result you get list with N references to same list.
To create a copy of lst you can use lst.copy(). Also list slice operator works same lst[:].
Shortened version of your code:
given = [1, 2, 3, 4, 5]
result = [given[0 : i + 1] for i in range(len(given))]
print(result)
Result:
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]]
The problem is that you are appending the list as such which is equivalent to appending the reference object to the original list. Therefore, whenever the original list is modified, the changes are reflected in the places where the reference is created, in this case in result. As you keep iterating via the for loop, all your references appended in result keep getting updated with the latest value of lst. The final result is that at the end of the for loop, you have appended 5 references to the original list lst and all of them store the latest value of lst being [1,2,3,4,5].
There are several ways to avoid this. What you need is to copy only the values. One of them is to use lst[:]. other way is to use lst.copy()
for item in given:
lst.append(item)
print(lst)
result.append(lst[:])
print (result)
# [[1], [1, 2], [1, 2, 3], [1, 2, 3, 4], [1, 2, 3, 4, 5]]
List is a mutable data type, there is only one copy in memory for a list unless you explicitly copy it to another variable. So
result.append(lst)
just appends a reference of the real copy and all the refercences point to the same copy.
In conclusion, you should learn about mutable/immutable data types and reference count in python.
Append lst.copy() gives the right output.
lst = []
given = [1,2,3,4,5]
result = []
for item in given:
lst.append(item)
print(lst)
result.append(lst.copy())
print(result)

Python - iterating over lists

I want my code's 2nd function to modify the new list made by my 1st function.
If I am understanding things correctly giving a list as an argument will give the original list (my_list in this case).
so the code removes 1 & 5 and then adds 6, but not 7?
my_list = [1, 2, 3, 4, 5]
def add_item_to_list(ordered_list):
# Appends new item to end of list which is the (last item + 1)
ordered_list.append(my_list[-1] + 1)
def remove_items_from_list(ordered_list, items_to_remove):
# Removes all values, found in items_to_remove list, from my_list
for items_to_remove in ordered_list:
ordered_list.remove(items_to_remove)
if __name__ == '__main__':
print(my_list)
add_item_to_list(my_list)
add_item_to_list(my_list)
add_item_to_list(my_list)
print(my_list)
remove_items_from_list(my_list, [1,5,6])
print(my_list)
output of
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6, 7, 8]
[2, 4, 6, 8]
instead of wanted
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6, 7, 8]
[2, 3, 4, 7, 8]
Thank you and sorry for the elementary question
In your remove_items_from_list function you are iterating through the wrong list. You should iterate through every item in the items_to_remove list like this:
def remove_items_from_list(ordered_list, items_to_remove):
# Removes all values, found in items_to_remove list, from my_list
for item in items_to_remove:
ordered_list.remove(item)
This will now iterate through each item in the remove list and remove it from you ordered_list.
There is a bug in the remove_items_from_list function. For it to achieve what you want it should go:
def remove_items_from_list(ordered_list, items_to_remove):
# Removes all values, found in items_to_remove list, from my_list
for item in items_to_remove:
ordered_list.remove(item)
As a side note, your code has incorrect number of blank lines before function definitions. Should be two blank lines before the function, and not more than one blank line inside functions. It seems not to have affected the code for now, but makes it harder to read, and could cause problems in future.
In the second function you want to iterate over items_to_remove (and not your original list) and then remove every item.
Use:
def remove_items_from_list(ordered_list, items_to_remove):
for item_to_remove in items_to_remove:
ordered_list.remove(item_to_remove)
And don't change the a list when you are iterating over it,which may cause bug.

Why does `mylist[:] = reversed(mylist)` work?

The following reverses a list "in-place" and works in Python 2 and 3:
>>> mylist = [1, 2, 3, 4, 5]
>>> mylist[:] = reversed(mylist)
>>> mylist
[5, 4, 3, 2, 1]
Why/how? Since reversed gives me an iterator and doesn't copy the list beforehand, and since [:]= replaces "in-place", I am surprised. And the following, also using reversed, breaks as expected:
>>> mylist = [1, 2, 3, 4, 5]
>>> for i, item in enumerate(reversed(mylist)):
mylist[i] = item
>>> mylist
[5, 4, 3, 4, 5]
Why doesn't the [:] = fail like that?
And yes, I do know mylist.reverse().
CPython list slice assigment will convert the iterable to a list first by calling PySequence_Fast. Source: https://hg.python.org/cpython/file/7556df35b913/Objects/listobject.c#l611
v_as_SF = PySequence_Fast(v, "can only assign an iterable");
Even PyPy does something similar:
def setslice__List_ANY_ANY_ANY(space, w_list, w_start, w_stop, w_iterable):
length = w_list.length()
start, stop = normalize_simple_slice(space, length, w_start, w_stop)
sequence_w = space.listview(w_iterable)
w_other = W_ListObject(space, sequence_w)
w_list.setslice(start, 1, stop-start, w_other)
Here space.listview will call ObjSpace.unpackiterable to unpack the iterable which in turn returns a list.

Categories