Python list bug or am I wrong? - python

I have constructed a 3 level nested list
#run on Python 3.2.3 32 bit on Win 7
L2=list(0 for i in range(2))
L3=list(L2 for i in range(3))
L4=list(L3 for i in range(4))
#give a new value to the very first number in list:
L4[0][0][0]=5
print("L4:")
print(L4)
#outputs erronously:
#[[[5, 0], [5, 0], [5, 0]], [[5, 0], [5, 0], [5, 0]], [[5, 0], [5, 0], [5, 0]], [[5, 0], [5, 0], [5, 0]]]
The same list given explicitly
#the same L4 given explicitly:
anotherL4=[[[0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]]]
print("anotherL4:")
#give a new value to the very first number:
anotherL4[0][0][0]=5
print(anotherL4)
#outputs correctly:
#[[[5, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0]]]

You're wrong. You've copied the reference multiple times, which means they're actually all the same list.

When you write list(L3 for i in range(4)), you are telling it to yield the same list L3 on each iteration of the generator comprehension. When you subsequently modify that list, the modifications show up everywhere, because all of them are references to the same list.
You could get the effect you seem to want by doing
list(list(L3) for i in range(4))
since using list(L3) makes a new list.

Just to elaborate,
a = 1
b = 2
c = [a,b]
a += 1
print c

Your problem is that you built a list of list references rather than a list of lists. Since the references all pointed back to a single list, when you mutate that single list, all the references show the change.
L0 = range(3)
L1 = range(3)
print(id(L0)) # prints a number
print(id(L1)) # prints a different number
print(id(L0) == id(L1)) # prints False
print(L0 is L1) # prints False; not same objects
print(L0 == L1) # prints True; values match
# your original code:
L2=list(0 for i in range(2))
L3=list(L2 for i in range(3))
L4=list(L3 for i in range(4))
print(L3[0] is L2) # prints True; L3[0] is a reference to L2
We can fix it and explicitly show what we are doing by using copy.deepcopy():
import copy
L2 = [0 for i in range(2)]
L3 = [copy.deepcopy(L2) for i in range(3)]
L4 = [copy.deepcopy(L3) for i in range(4)]
#give a new value to the very first number in list:
L4[0][0][0]=5
print("L4:")
print(L4)
Note that instead of making a generator expression and passing it to list() to force it to be expanded out to a list, I just used list comprehensions in the above code to directly make lists.
More usually if you want to do this crazy thing, you should maybe just nest some list comprehensions:
L4 = [[[0 for _ in range(2)] for _ in range(3)] for _ in range(4)]
This makes it pretty clear that we are building a new list of lists of lists. And if you use copy.deepcopy() you are basically just copying a bunch of zeroes, so you might as well just build new lists using zeroes.

Related

Confusion about variable in python. How python use variables? [duplicate]

This question already has answers here:
What is the difference between shallow copy, deepcopy and normal assignment operation?
(12 answers)
Closed 3 years ago.
When I run this,
key=[[0, 0, 0], [1, 0, 0], [0, 1, 1]]
key_=[[0, 0, 0], [1, 0, 0], [0, 1, 1]]
m=3
for i_ in range(1,m+1):
for j_ in range(1,m+1):
key[j_-1][m+1-i_-1]=key_[i_-1][j_-1]
print(key,key_,sep='\n')
I got this:
>>> [[0, 1, 0], [1, 0, 0], [1, 0, 0]]
[[0, 0, 0], [1, 0, 0], [0, 1, 1]]
However, when I changed only the second line,
key=[[0, 0, 0], [1, 0, 0], [0, 1, 1]]
key_=key
m=3
for i_ in range(1,m+1):
for j_ in range(1,m+1):
key[j_-1][m+1-i_-1]=key_[i_-1][j_-1]
print(key,key_,sep='\n')
I got this:
>>> [[0, 1, 0], [0, 0, 0], [0, 0, 0]]
[[0, 1, 0], [0, 0, 0], [0, 0, 0]]
Why do the two codes have different results?
In my opinion, they should be same.
Why the different parts make different results?
In Python, variables which contain lists are more like pointers. When you say key_=key, you're telling Python to use the same list in both cases, not a copy. If you want a copy of the original list, use key_=[x.copy() for x in key]. This will copy the contents of each item (which are lists) in key.
Examples:
Make two lists which contain 1, 2 and 3
>>> my_list = [1,2,3]
>>> my_list
[1, 2, 3]
>>> my_list2 = my_list
>>> my_list2
[1, 2, 3]
Now let's edit the value of the original list...
>>> my_list[0] = 10
>>> my_list
[10, 2, 3]
>>> my_list2
[10, 2, 3]
The changes are copied because my_list and my_list2 are the same, not just a copy of it.
Let's change my_list2:
>>> my_list2[1] = 20
>>> my_list2
[10, 20, 3]
>>> my_list
[10, 20, 3]
And once again, the values are updated between the two as they both point to the same list.
Now let's see what happens if we use the copy method:
>>> my_list_copy = my_list.copy()
>>> my_list_copy
[10, 20, 3]
>>> my_list[0] = 1
>>> my_list
[1, 20, 3]
>>> my_list_copy
[10, 20, 3]
And so we can see that the two lists start with the same contents, but are different.
Using the is operator, we can also see this difference between the 3 list variables:
>>> my_list is my_list2
True
>>> my_list is my_list_copy
False
>>>
EDIT:
key is a list containing lists as items. When the copy method is called, only the outer list is copied, so the actual items in both lists (ie the sub-lists) are identical. To copy these by value rather than effectively by reference, we can use a simple list comprehension:
key_=[x.copy() for x in key]
This code copies each item in key by value, and creates a new list with these as items.
The output using this is
[[0, 1, 0], [1, 0, 0], [1, 0, 0]]
[[0, 0, 0], [1, 0, 0], [0, 1, 1]]

How to iterate over a list that has duplicate values?

This is probably a very basic question but I dont know what I have to search for to find the answer for it:
I have this code:
list = [[0,1],[0,2],[1,3],[1,4],[1,5]]
list.append(list[0])
for i in list:
i.append(0)
print(list)
This List will later be used as coordinates for a curve. I need to duplicate the first coordinate at the end to get a closed curve.
If I then want to add a third value to each coordinate in the list the first and last item in list will be iterated over twice:
[[0, 1, 0, 0], [0, 2, 0], [1, 3, 0], [1, 4, 0], [1, 5, 0], [0, 1, 0, 0]]
I am guessing they have the same memory address and thereby the append-function is applied to the same object at this address once for the first index and once for the last.
What is this phenomenon called ? what is the easiest way to get the list like this:
[[0, 1, 0], [0, 2, 0], [1, 3, 0], [1, 4, 0], [1, 5, 0], [0, 1, 0]]
Thank you for your help
You can do a list comprehension:
list = [[0,1],[0,2],[1,3],[1,4],[1,5]]
list.append(list[0])
list = [x + [0] for x in list]
print(list)
# [[0, 1, 0], [0, 2, 0], [1, 3, 0], [1, 4, 0], [1, 5, 0], [0, 1, 0]]
EDIT: The trick here is, using x + [0] within the list comprehension. This way new lists are created, thus you do not append 0 to the same list twice (Hattip to #dx_over_dt)
The problem you have with your approach is, that the first and last element of your list refers to the very same object. You can see this, when you print i and list for every iteration:
for i in list:
i.append(0)
print(i)
print(list)
So for the first and last i in your loop, you will append a 0 to the very same list.
You could stick to your approach appending a copy of the first element:
list.append(list[0].copy())
The simplest answer is to add the 0's before appending the closing point.
list = [[0,1],[0,2],[1,3],[1,4],[1,5]]
for i in list:
i.append(0)
list.append(list[0])
print(list)
It's the tiniest bit more efficient than a list comprehension because it's not making copies of the elements.

Why can't I assign to 3d lists properly? [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 3 years ago.
Let's say that I make a 3D list
list = [[[0, 0], [0, 0]], [[0, 0], [0, 0]]]
and I run
list[0][0][0] = 1 #set the first element of the first list of the first list to 1
print(list)
I'd expect to get
[[[1, 0], [0, 0]], [[0, 0], [0, 0]]]
but instead, I get
[[[1, 0], [1, 0]], [[1, 0], [1, 0]]]
Can someone figure out how to make it assign a variable to ONLY ONE element of a 3D list, instead of every first element? Thanks!
If it matters, I'm using Python 3.7 32-bit.
I have reproduced your results by making an assumption about how you actually defined your list. I assume that you defined some variable such as ab below and used that to create your list. However, the new list is still a bunch of references to your ab variable, so changing one actually changes ab which will affect your whole list.
ab = [0,0]
mylist = [[ab,ab],[ab,ab]]
mylist[0][0][0] = 1
print(mylist," ",ab)
OUTPUT
[[[1, 0], [1, 0]], [[1, 0], [1, 0]]] [1, 0]
To resolve this, simple initialize your lists with 0 instead of some variable:
mylist = [[[0,0],[0,0]],[[0,0],[0,0]]]
or
mylist = [[[0 for _ in range(2)] for _ in range(2)] for _ in range(2)]

How can I change the view of my list?

Good evening! I have the following code which works when you write
python new.py -s 13 -p 5 on the command prompt.
What command prompt prints is :
[[1, [0], [0]], [1, [0], [0]], [1, [0], [0]], [1, [0]], [1, [0]]]
But I wanted:
[[1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0], [1, 0]]
How can I do this?
-s 12 is the length of the string and -p 7 the 1s.
Thank you!
My code sample :
import argparse
p = argparse.ArgumentParser()
p.add_argument("-pulses", help = "number of pulses", type = int)
p.add_argument("-slots", help = "length of the rythm", type = int)
args = p.parse_args()
slots = args.slots
pulses = args.pulses
pauses = slots - pulses
mod = slots % pulses
rhythm = []
if mod != 0:
i = 0
j = 0
temp = []
while i < pulses:
rhythm.append([1])
i = i + 1
while j < pauses:
rhythm.append([0])
j = j + 1
m = slots
n = pauses
while (rhythm[-1]==[0]):
if (n!=0):
step = m%n
hlp = n
m = n
n = step
i = 0
while (i<step):
rhythm[i].append(rhythm[-1])
rhythm.remove(rhythm[-1])
i = i + 1
print (rhythm)
Note: This is a mere copy&paste from the comment.
Check this out.
I haven't fully analyzed your code but I believe your problem resides in append(). Try to replace it for extend().
The Problem is this line
rhythm[i].append(rhythm[-1])
rhythm[-1] returns a list ([0] or [1]). So you must use extend instead of append.
rhythm[i].extend(rhythm[-1])
Python List
Woozy Coder definitively correctly answered this question.
More generally speaking about lists appending and extending:
dest_list.append(appended_item) appends an item to the list. Should the appended item be a list, this list will be appended as is, and becomes an additional item at the end of the destination list.
dest_list.extend(list_extension) extends a list with another list, every item of this other list being individually appended to the end of the destination list.
Your code seems overly complicated. I think the code below does what you're trying to do:
def rhythm_list(slots, pulses):
q, r = divmod(slots, pulses)
full = [1] + [0] * q
part = full[:-1]
return [full] * r + [part] * (pulses - r)
# Test
print(rhythm_list(13, 5))
print(rhythm_list(12, 7))
print(rhythm_list(12, 4))
output
[[1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0], [1, 0]]
[[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1], [1]]
[[1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0]]
Note that the output list contains duplicated references to the full and part lists, so if you do:
m = rhythm_list(12, 7)
m[0][0]=2
then m becomes:
[[2, 0], [2, 0], [2, 0], [2, 0], [2, 0], [1], [1]]
If this behaviour is undesirable, then change the last line of rhythm_list to
return [full[:] for _ in range(r)] + [part[:] for _ in range(pulses - r)]

Populate a 5 * 5 list with python

I have a list[5][5] to populate... it looks like a table with 5 columns and 5 rows.
Each cell can be either one or zero.
I want to find different 2^25 possibility that can exist. Each possiblity is a combination of either 0 or 1 in a 5*5 table/list
How can I do that? With nested loop or something?
I suggest you start small... with a 1x1 list first and check that you can display both of the available combinations:
[[0]]
[[1]]
Next up, try a 2x2 list. There are 16 different lists to display:
[[0, 0], [0, 0]]
[[0, 0], [0, 1]]
[[0, 0], [1, 0]]
[[0, 0], [1, 1]]
[[0, 1], [0, 0]]
[[0, 1], [0, 1]]
[[0, 1], [1, 0]]
[[0, 1], [1, 1]]
[[1, 0], [0, 0]]
[[1, 0], [0, 1]]
[[1, 0], [1, 0]]
[[1, 0], [1, 1]]
[[1, 1], [0, 0]]
[[1, 1], [0, 1]]
[[1, 1], [1, 0]]
[[1, 1], [1, 1]]
If you've got the algorithm right for 1x1 and 2x2, then you should be able to generalise it to print your 5x5.
Good luck!
Update
Since you appear to be still struggling, here's a little extra help.
Break this problem into smaller problems. I'd start with generating the values. If you ignore the list notation in my examples above, you'll see that the sequence of values is one that is recognisable to every computer scientist on the planet. It's also pretty easy to generate in Python using bin() and str.zfill().
The second problem is putting them into lists. This isn't too hard either. Supposing the first value in your sequence is '0000'. You know that your lists are two rows by two columns. You can put the first two characters into a list and put that list into a list. Then put the next two characters into a list and append that list to the previous one. Done. Repeat for each value in the sequence.
Hope this helps.
You could try:
import itertools
gen = itertools.product((0,1),repeat=25)
To create a generator to get all of the combinations in 1d and then reshape the data as needed.

Categories