Unexpected list append - python

import random
stats = []
statslist = []
rollslist = []
for var1 in range(4):
stats.append(random.randrange(1, 7))
rollslist.append(stats)
print(stats)
b = min(stats)
stats.remove(b)
print(sum(stats))
statslist.append(sum(stats))
print(stats)
print(rollslist)
print(statslist)
actual result
[5, 1, 1, 3]
9
[5, 1, 3]
[[5, 1, 3]]
[9]
expected result
[5, 1, 1, 3]
9
[5, 1, 3]
[[5, 1, 1, 3]]
[9]
I'm expecting it to print four numbers for the fourth result instead of the three it's giving me. I appended the list before the number was removed. What am I missing?

You added a mutable list. When you modified it later, the modification affected the object you placed in the list, because it was a direct reference and not a copy.
The easiest way to make a copy of a list is to use slicing:
rollslist.append(stats[:])

as Avinash mentioned, it's because you're still referring to the stats list. Meaning, changes made later (like the removal of an item) will still be reflected. For the expected behaviour, you can make a copy of the list like so:
newCopy = list(stats)
rollslist.append(newCopy)

Related

inserted list changes even after being inserted

I wanted to create a list of lists
def func(m,n,l,t):
a=[i for i in range(m)]
b=[]
b.append(a)
a=swap(a)
b.append(a)
for i in b:
print(i)
def swap(l):
i,n=0,len(l)
while(i<n):
l[i],l[i+1]=l[i+1],l[i]
i+=2
return l
i created list a as my basis and append each modification i want to in the b list.
The problem is after i append the first list and modify it, the first one i inserted also changes the same as the second one i inserted.
the output of this code is this
[1, 0, 3, 2, 5, 4, 7, 6]
[1, 0, 3, 2, 5, 4, 7, 6]
what i want is when i performm the swap i dont want the first list to change
the output should look like this
[0, 1, 2, 3, 4, 5, 6, 7]
[1, 0, 3, 2, 5, 4, 7, 6]
if you could help, please thank you.
In place of append() method, you can try out extend() method which does the same as of append. but extend() method is mainly used for iterate items, as of in your case.
and also if you want to use only append, then take a copy of your variable first and append it, as you are changing in the first place, it is taking effect in the list too. So you can follow append into a list like, a.copy(). Hopes it helps
Please try out and share the feedback.
Thank you
Their are multiple code error you have done, for example:
you don't have to iterate over range object in order to get list, you could just...
a = list(range(m))
and also You don't have to run a while loop in order to iterate over two steps you could...
for i in range(0, len(l), 2):
see range for reference
also you didn't use any of n, l, t parameters of the func function.
THE ANSWER
when you pass the a variable to the swap function you are actually passing the class Object itself, so the swap function can change the value of the a variable (list class).
All you need is passing a copy of the a variable not the variable itself
a = swap(a.copy())
FINAL CODE
def swap(l):
for i in range(0, len(l), 2):
l[i],l[i+1]=l[i+1],l[i]
return l
def func(m):
a = list(range(m))
b = [a]
a = swap(a.copy())
b.append(a)
for i in b:
print(i)
func(8) # [0, 1, 2, 3, 4, 5, 6, 7], [1, 0, 3, 2, 5, 4, 7, 6]

How to append values to list?

I want to append my list with values for each loop iteration:
for i in range (4,10):
a_list = [1,2,3]
a_list = a_list.append(i)
The wanted output would be [1,2,3,4,5,6,7,8,9]. But I get None. Also printing type(a_list) after using .append() gives me <class'NoneType'>. What is the problem here ?
firstly, you have to mention a_list before for. instead, you will get [1, 2, 3, 9]. secondly, you give the a_list a value of a_list.append() function.
a_list = [1, 2, 3]
for i in range(4, 10):
a_list.append(i)
it is mainly because of the fact that list.append method does not return anything, it appends the given value to the list in-place.
we can confirm this by the simple example below
a = list()
b = a.append(5)
>> print(b)
None
>> print(a)
[5]
As matter of fact,there is a simpler and more performant way to achieve this
>> a_list = [1,2,3]
>> a_list += list(range(4, 10))
>> print(a_list)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Here, we first shape a new list from a iterator range(4, 10),
then we concate the two list together to get the list we want by adding the new list to the original list a_list.
by doing this, we can avoid the overhead caused by repeatedly call the list.append method in a for loop
a_list = []
for i in range (1,10):
a_list.append(i)
print(a_list)
Output
[1, 2, 3, 4, 5, 6, 7, 8, 9]
You have the wrong indentation on the loop.You need to add i at each iteration. Also, to get the list you want, you need to add 1, then 2, then 3, and so on. i this is what needs to be added. Put print(i) and print each iteration.
a_list = [1,2,3]
for i in range (4,10):
a_list.append(i)
print(a_list)
If you use your option, it will be correct to declare an array once. And then only add values. See below.
[1, 2, 3, 4, 5, 6, 7, 8, 9]

How can I add two elements in a list in this manner?

So I'm trying to find how can I append two items into my new list r under the function histogram. I've learnt that using extend, I am able to use commas to add more than one element each time. However, when I tried to do this, I get an error message saying
TypeError: list.extend() takes exactly one argument (2 given)
What am I doing wrong here?? Am I misunderstanding the syntax of the list.extend function?
Here is my code btw..
def reverse(filename):
s = open(filename, 'r')
content = s.read()
return list(content)
print(reverse('data'))
def histogram(filename):
g: list = reverse(filename)
r = []
for x in g:
r.extend(x, g.count(x))
return r
print(histogram('data'))
As the error message mentions, extend receives one argument only, a list, and concatenate it to the list that calls the function. If you want to add a single element to a list, use append. For example:
a = [1, 2]
b = [3, 4]
b.extend(a)
print(b) # [3, 4, 1, 2]
b.append(5)
print(b) # [3, 4, 1, 2, 5]
In order to add 2 elements into your list, you can do:
r.extend([x, g.count(x)])
In addition to answer given by #Gabip,
"I've learnt that using extend, I am able to use commas to add more than one element each time", you can consider it as the incomplete information of the extend function.
To further clarify between extend and append. append adds the argument as a single value at the end of the list and extend iterates over its argument and add it at the end of the list.
x = [1, 2, 3]
"""list.append"""
x.append(2)
>>> [1, 2, 3, 2]
x.append([2, 3])
>>> [1, 2, 3, [2, 3]]
"""list.extend"""
x.extend(2)
>>> TypeError: 'int' object is not iterable
x.extend([2])
>>> [1, 2, 3, 2]
x.extend([ [2] ])
>>> [1, 2, 3, [2]]

Python: Why still list elements are not disappeared after using procedure?

I define this function to do: [1,2,3] --> [2,3,1]
def shift_to_left(p):
p.append(p[0])
return p[1:]
When I check like this, results are ok:
p1 = [1,2,3]
print p1
p1 = shift_to_left(p1)
print p1
The result:
[1, 2, 3]
[2, 3, 1]
However, when I introduce another list and concatenate as I go the result is different:
ss = []
p1 = [1,2,3]
ss.append(p1)
p1 = shift_to_left(p1)
ss.append(p1)
print ss
The result
[[1, 2, 3, 1], [2, 3, 1]]
But I want:
[1,2,3]
[2,3,1]
why is it happening?
Thanks very much,
In Python, most arguments are taken by reference.
Your function, shift_to_left, actually mutates its argument (through the use of append), but then returns a slice (which is a shallow copy of the list).
When you replace your original variable with the output of shift_to_left, this behaviour is hidden:
In [1]: def shift_to_left(p):
...: p.append(p[0])
...: return p[1:]
...:
In [2]: xs = [1, 2, 3]
In [3]: xs = shift_to_left(xs)
In [4]: xs
Out[4]: [2, 3, 1]
But if we instead assign the result into a new variable, we can see that the original list has indeed been changed:
In [5]: ys = shift_to_left(xs)
In [6]: ys
Out[6]: [3, 1, 2]
In [7]: xs
Out[7]: [2, 3, 1, 2]
Our result, ys, is the slice of xs from the second element onwards. That's what you expected.
But xs itself has also been changed by the call to append: it's now one element longer than before.
This is what you're experiencing in your second example.
If you do not want this behaviour, one way of avoiding it is by passing a copy of your list to shift_to_left:
In [8]: zs = shift_to_left(ys[:])
In [9]: zs
Out[9]: [1, 2, 3]
In [10]: ys
Out[10]: [3, 1, 2]
Here, you can see that the original list ys has not been modified, as shift_to_left was given a copy of it, not the object itself. (This is still passing by reference, of course; it's just not a reference to ys).
Alternatively, and probably more reasonably, you could change shift_to_left itself, so that it does not modify its arguments:
def shift_to_left(xs):
return xs[1:] + xs[0] # build a new list in the return statement
The big problem with both these approaches is that they create lots of copies of lists, which can be incredibly slow (and use a lot of memory) when the lists are large.
Of course, as #Marcin points out, if this is more than an academic exercise, you should probably use one of the built-in data structures such as deque.
If you want to shift/rotate elements in a list, I think better would be to use a deque, rather than reinvent the wheel. For example:
from collections import deque
d = deque([1,2,3])
d.rotate(-1)
print(d)
#[2, 3, 1]
If you run your code here, you can notice that ss remains pointing to the original (mutated in your shift function because of p.append(p[0])) copy of p1, where as p1 points to a knew list all together when it gets reassigned, resulting in the behavior. (Step 10 out of 11)
(p becomes mutated, and ss[0] = p)
(p1 gets assigned to a new list altogether, which is latter appended to ss)
why is it happening?
return p[1:] is "non-destructive": it creates a new list. However, p.append(p[0]) is "destructive": it changes p itself.
First you append p1 to ss. This makes [[1, 2, 3]], where [1, 2, 3] is p1.
Then you do your shift_to_left, which changes p1 to [1, 2, 3, 1] and returns [2, 3, 1]. Because p1 is contained in ss, your ss becomes [[1, 2, 3, 1]], and then you append the new p1 to form [[1, 2, 3, 1], [2, 3, 1]].
A better implementation would be purely non-destructive:
def shift_to_left(p):
return p[1:] + [p[0]]
Try this:
p1 = [1,2,3]
p1 = shift_to_left(p1)
ss = []
ss.extend(p1)
print ss
That prints [2, 3, 1]. Use extend() instead because append() will create an array in an array. Also you had an extra call to ss.append(p1).

reverse method mutating input

For an assignment we were asked to create a function that would reverse all the elements in an arbitrarily nested list. So inputs to the function should return something like this:
>>> seq = [1,[2,[3]]]
>>> print arb_reverse(seq)
[[[3],2],1]
>>> seq = [9,[[],[0,1,[[],[2,[[],3]]]],[],[[[4],5]]]]
>>> print arb_reverse(seq)
[[[[5,[4]]],[],[[[[3,[]],2],[]],1,0],[]],9]
I came up with a recursive solution which works well:
def arb_reverse(seq):
result = []
for element in reversed(seq):
if not is_list(element):
result.append(element)
else:
result.append(arb_reverse(element))
return result
But for a bit of a personal challenge I wanted to create a solution without the use of recursion. One version of this attempt resulted in some curious behavior which I am not understanding. For clarification, I was NOT expecting this version to work properly but the resulting input mutation does not make sense. Here is the iterative version in question:
def arb_reverse(seq):
elements = list(seq) #so input is not mutated, also tried seq[:] just to be thorough
result = []
while elements:
item = elements.pop()
if isinstance(item, list):
item.reverse() #this operation seems to be the culprit
elements += item
else:
result.append(item)
return result
This returns a flattened semi-reversed list (somewhat expected), but the interesting part is what it does to the input (not expected)...
>>> a = [1, [2, [3]]]
>>> arb_reverse(a)
[2, 3, 1]
>>> a
[1, [[3], 2]]
>>> p = [1, [2, 3, [4, [5, 6]]]]
>>> print arb_reverse(p)
[2, 3, 4, 5, 6, 1]
>>> print p
[1, [[[6, 5], 4], 3, 2]]
I was under the impression that by passing the values contained in the input to a variable using list() or input[:] as i did with elements, that I would avoid mutating the input. However, a few print statements later revealed that the reverse method had a hand in mutating the original list. Why is that?
The list() call is making a new list with shallow-copied lists from the original.
Try this (stolen from here):
from copy import deepcopy
listB = deepcopy(listA)
Try running the following code through this tool http://people.csail.mit.edu/pgbovine/python/tutor.html
o1 = [1, 2, 3]
o2 = [4, 5, 6]
l1 = [o1, o2]
l2 = list(l1)
l2[0].reverse()
print l2
print l1
Specifically look at what happens when l2[0].reverse() is called.
You'll see that when you call list() to create a copy of the list, the lists still reference the same objects.

Categories

Resources