Strange behavior of python's append? - python

size = input('Kirjainmäärä: ')
kirjaimet = "0ABCDEFGHIJKLMNOPQRSTUVWXYZ"
ruudukko = []
rivi = ['0']*(size*2-1)
for i in range(0,size): #produce n rows
for y in range(0,i+1): #this row needs i+1 letters
for z in range(0+i,size*2-1-i):
rivi[z] = kirjaimet[size-i]
print rivi, i
ruudukko.append(rivi)
print ruudukko
So the code was supposed to produce number matrices of the format
input=4
output=
DDDDDDD
DCCCCCD
DCBBBCD
DCBABCD
so here's the output of the above code with input=5 (print rivi, i -part)
['E', 'E', 'E', 'E', 'E', 'E', 'E', 'E', 'E'] 0
['E', 'D', 'D', 'D', 'D', 'D', 'D', 'D', 'E'] 1
['E', 'D', 'C', 'C', 'C', 'C', 'C', 'D', 'E'] 2
['E', 'D', 'C', 'B', 'B', 'B', 'C', 'D', 'E'] 3
['E', 'D', 'C', 'B', 'A', 'B', 'C', 'D', 'E'] 4
everythings ok so far, but then..
[['E', 'D', 'C', 'B', 'A', 'B', 'C', 'D', 'E'], ['E', 'D', 'C', 'B', 'A', 'B', 'C', 'D', 'E'], ['E', 'D', 'C', 'B', 'A', 'B', 'C', 'D', 'E'], ['E', 'D', 'C', 'B', 'A', 'B', 'C', 'D', 'E'], ['E', 'D', 'C', 'B', 'A', 'B', 'C', 'D', 'E']]
The appended list ruudukko comes out all filled with the object appended last!
What the hell's going on?

You aren't appending five different lists to your result. You are appending the same list five times.
You can fix it by appending a copy of your list to the result:
ruudukko.append(rivi[:])
See it working online: ideone

list.append() stores a reference to the object appended. So you have stored multiple references to your list rivi in your list ruudukko. Naturally, since they are all actually the same object, when you change one, it changes everywhere it is referenced.
Make a fresh rivi list each time through your loop.
This behavior is not strange; it is normal and expected and should be covered in any decent Python tutorial.

You've only declared rivi one time, then repeatedly reassigned its value. You have this single object, which is mutable, appended repeatedly into your outer list ruudukko. Because the object is mutable and you're not copying it you end up with the final value in every position in ruudukko.

You are putting the same object in the list ruudukko five times. So each of the 5 items of ruudukko are in fact bound to the same object.
Because list is a mutable type, you are able to modify it and every other reference to that same object appear to be modified also. Of course it's only one object being modified, it just looks to you as though each item in ruudukko has been modified. That's not the case, the same object appears there five times.
In order to fix this you need to create a new list object each time you append to ruudukko. There are lots of ways to do this, for example you could do it like this:
ruudukko.append(list(rivi))
The most simple example I can think of that illustrates the behaviour of mutable objects is this:
>>> a = [0]
>>> b = a
>>> b[0] = 1
>>> a
[1]

Related

Iterating through a list for specific instances

I have the following code:
paths = [['E', 'D', 'A', 'B'], ['E', 'D', 'A', 'C', 'B'], ['E', 'D', 'B'], ['E', 'D', 'C', 'B'], ['E', 'B'], ['E', 'C', 'B']]
Now, the lists inside a list represent node paths from start to end which were made using Networkx, however that is some background information. My question is more specific.
I am trying to derive the lists that only have every letter from A-E, aka it would return only the list:
paths_desired = [['E', 'D', 'A', 'C', 'B']]
If I were to have another path:
paths = [['E', 'D', 'A', 'B'], ['E', 'D', 'A', 'C', 'B'], ['D', 'B', 'A','C','E'], ['A', 'D', 'C', 'B']]
It would return:
paths_desired = [['E', 'D', 'A', 'C', 'B'],['D', 'B', 'A', 'C', 'E']]
My idea is a for loop that iterates through each list:
for i in pathways:
counter = 0
for j in letters:
if j in i:
counter = counter + 1;
if counter == 5:
desired_paths.append(i)
print(desired_paths)
This works, however, I want to make the loop more specific, meaning I want only lists that have the following order: ['E','D','A','C','B'], even if all the letters are present in a different list, within the paths list.
Additionally, is there a way I can upgrade my for loop, so that I wouldn't count, rather check if the letters are in there, and not more than 1 of each letter? Meaning no multiple Es, no multiple D, etc.
You can use a use a set and .issubset() like this:
def pathways(letters, paths):
ret = []
letters = set(letters)
for path in paths:
if letters.issubset(path):
ret.append(path)
return ret
letters = ['A', 'B', 'C', 'D', 'E']
paths = [['E', 'D', 'A', 'B'], ['E', 'D', 'A', 'C', 'B'],
['D', 'B', 'A','C','E'], ['A', 'D', 'C', 'B']]
print(pathways(letters, paths)) # => [['E', 'D', 'A', 'C', 'B'], ['D', 'B', 'A', 'C', 'E']]
Also, as a comment by ShadowRanger pointed out, the pathways() function could be shortened using filter(). Like this:
def pathways(letters, paths):
return list(filter(set(letters).issubset, paths))
letters = ['A', 'B', 'C', 'D', 'E']
paths = [['E', 'D', 'A', 'B'], ['E', 'D', 'A', 'C', 'B'],
['D', 'B', 'A','C','E'], ['A', 'D', 'C', 'B']]
print(pathways(letters, paths))

Reordering a list in a given order

I want to reorder my list in a given order,
For example I have a list of ['a', 'b', 'c', 'd', 'e', 'f', 'g']
this has an index of [0,1,2,3,4,5,6] and lets say the new ordered list would have an order of [3,5,6,1,2,4,0] which would result in ['d','f','g', 'b', 'c', 'e', 'a'].
How would you result in such code?
I thought of using for loop by doing the
for i in range(Len(list))
and after that I thought go using append or creating a new list? maybe but I'm not sure if I'm approaching this right.
All you need to do is iterate the list of indexes, and use it to access the list of elements, like this:
elems = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
idx = [3,5,6,1,2,4,0]
result = [elems[i] for i in idx]
print(result)
Output:
['d', 'f', 'g', 'b', 'c', 'e', 'a']
import numpy as np
my_list = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
def my_function(my_list, index):
return np.take(my_list, index)
print(my_function(my_list, [3,5,6,1,2,4,0]))
Output: ['d' 'f' 'g' 'b' 'c' 'e' 'a']

How to eliminate the items in the python list step by step?

I am using a 'for loop' to eliminate the item step by step and generate a new list(feature_combination) including different combinations.
feature_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
feature_combination = []
for i in range(7):
feature_list.pop()
feature_combination.append(feature_list)
feature_combination
The ideal output should be:
[['A', 'B', 'C', 'D', 'E', 'F'],['A', 'B', 'C', 'D', 'E'],['A', 'B', 'C', 'D'],['A', 'B', 'C'],['A', 'B'],['A'], []]
But the current output is:
[[], [], [], [], [], [], []]
When I print the progress step by step:
feature_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
feature_combination = []
for i in range(7):
feature_list.pop()
print(feature_list)
I can get the following the results:
['A', 'B', 'C', 'D', 'E', 'F']
['A', 'B', 'C', 'D', 'E']
['A', 'B', 'C', 'D']
['A', 'B', 'C']
['A', 'B']
['A']
[]
So, why I cannot append these results to an empty list? What is the problem?
It's because when you call feature_combination.append(feature_list), you are appending a reference to feature_list, not the actual value of feature_list. Since feature_list is empty at the end of the for loop, all of the references to it are empty as well.
You can fix it by changing feature_combination.append(feature_list) to feature_combination.append(feature_list.copy()), which makes a copy of the list to store.
First of all, you need to pass an index into pop in order to specify which element to delete. Though I find this unesaccary, instead you could use slicing.
Below is an example of how you could accomplish your goal. This code adjusts to your desired output.
feature_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
feature_combination = []
for i in range(7):
feature_list = feature_list[:-1]
feature_combination.append(feature_list)
print(feature_combination)
output
[['A', 'B', 'C', 'D', 'E', 'F'], ['A', 'B', 'C', 'D', 'E'], ['A', 'B', 'C', 'D'], ['A', 'B', 'C'], ['A', 'B'], ['A'], []]
A Python variable is a symbolic name that is a reference or pointer to an object. Once an object is assigned to a variable, you can refer to the object by that name. But the data itself is still contained within the object. refer this.
This is because the feature_list points to a specific object, which keeps updating as you pop are subsequently. You are basically creating a list that contains [object, object, object ...] all pointing to the same feature_list object. As you keep popping and updating the object, the list that collects multiple instances of this same object also gets updated with this object.
Here is how you can test this happening -
feature_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
feature_combination = []
for i in range(7):
feature_list.pop()
feature_combination.append(feature_list)
print('iteration', i)
print(feature_combination) #Print the primary list after each iteration
iteration 0
[['A', 'B', 'C', 'D', 'E', 'F']]
iteration 1
[['A', 'B', 'C', 'D', 'E'], ['A', 'B', 'C', 'D', 'E']]
iteration 2
[['A', 'B', 'C', 'D'], ['A', 'B', 'C', 'D'], ['A', 'B', 'C', 'D']]
iteration 3
[['A', 'B', 'C'], ['A', 'B', 'C'], ['A', 'B', 'C'], ['A', 'B', 'C']]
iteration 4
[['A', 'B'], ['A', 'B'], ['A', 'B'], ['A', 'B'], ['A', 'B']]
iteration 5
[['A'], ['A'], ['A'], ['A'], ['A'], ['A']]
iteration 6
[[], [], [], [], [], [], []]`
Notice, that after each iteration, every instance of the sublist is being updated after the pop and reflect inside the main list.
A fix
A fix is to use a slice to get and store a copy.
feature_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
feature_combination = []
for i in range(7):
feature_list.pop()
print(feature_list)
feature_combination.append(feature_list[:]) #<----
feature_combination
[['A', 'B', 'C', 'D', 'E', 'F'],
['A', 'B', 'C', 'D', 'E'],
['A', 'B', 'C', 'D'],
['A', 'B', 'C'],
['A', 'B'],
['A'],
[]]

Extend: merge strings on a list from other lists (or variables) at the same time [duplicate]

This question already has answers here:
Possible to append multiple lists at once? (Python)
(7 answers)
Closed 6 years ago.
I have 3 different lists and I need to merge them. It is easy when you need to extend a list with just an element or adding an inteire list. But with more lists or adding a variable in the middle, it seems impossible.
list1 = [ 'a', 'b', 'c']
list2 = [ 'd', 'e', 'f']
list3 = ['g', 'h', 'i']
Adding just one list:
list1.extend(list3)
Return:
['a', 'b', 'c', 'g', 'h', 'i']
Adding two lists:
list1.extend((list2,list3))
Return two lists inside another list:
['a', 'b', 'c', ['d', 'e', 'f'], ['g', 'h', 'i']]
Adding two list with operator '+':
list1.extend((list2 + list3))
Returns
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
but if you need to do something like:
list1.extend(('test' + list2 + fetch[0] + list3 + etc, etc, etc))
will not works.Can't concatenate.
A temporary solution adding a loop could be:
for l1 in list2:
list1.extend(l1)
for l2 in list3:
list1.extend(l2)
to finally have:
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
Clearly a waste of lines and cycles
Is there a more efficient way to archive that without using external modules?
EDIT: the example of simple lists is just to understand what basically I need. The real problem is adding string or number or index on a single line of '.extend'.
SOLVED:
Wayne Werner drive me to the right direction to concatenate different type of elements.
list1 = [ 'a', 'b', 'c']
list2 = [ 'd', 'e', 'f']
list3 = ['g', 'h', 'i']
for other_list in (['test'], str(1), list2[2], list2[1]):
list1.extend(other_list)
Result:
['a', 'b', 'c', 'test', '1', 'f', 'e']
If you're looking for a clean way of extending a single list with N number of other lists, it's as simple as that:
my_list = ['a', 'b', 'c']
for other_list in (some_iterable, some_other_iterable, another_iterable):
my_list.extend(other_list)
I can't think of any more reasonable solution than that.
Just use extend for each of the lists you want to add on:
list1.extend(list2)
list1.extend(list3)
You can extend one of the lists with the addition of the other two:
list1 = [ 'a', 'b', 'c']
list2 = [ 'd', 'e', 'f']
list3 = ['g', 'h', 'i']
list3.extend(lis1 + list2)

Switch first and last elements of two lists in Python

In Python, I have two lists that either have equal number of elements (e.g. 8 and 8) or one less than the other (e.g. 7 and 8; 3 and 4):
list1 = ['A', 'B', 'C', 'D']
list2 = ['E', 'F', 'G', 'H']
or
list3 = ['A', 'B', 'C']
list4 = ['D', 'E', 'F', 'G']
I'm trying to figure out the best way to build an algorithm that will switch the last half of the first list with the first half of the last list, resulting in this, when both lists have an even number of elements:
switched_list1 = ['A', 'B', 'E', 'F']
switched_list2 = ['C', 'D', 'G', 'H']
…and this when the one of the lists has an odd number:
switched_list3 = ['A', 'D', 'E']
switched_list4 = ['B', 'C', 'F', 'G']
What's the most efficient way to build an algorithm that can switch list elements like this?
list1 = ['A', 'B', 'C']
list2 = ['D', 'E', 'F', 'G']
nlist1 = len(list1)/2
nlist2 = len(list2)/2
new1 = list1[:nlist1] + list2[:nlist2]
new2 = list1[nlist1:] + list2[nlist2:]
print new1
print new2
produces
['A', 'D', 'E']
['B', 'C', 'F', 'G']
>>> def StrangeSwitch(list1,list2):
return (list1[:len(list1)/2]+list2[:len(list2)/2],list1[len(list1)/2:]+list2[len(list2)/2:])
>>> list1 = ['A', 'B', 'C', 'D']
>>> list2 = ['E', 'F', 'G', 'H']
>>> (list1,list2)=StrangeSwitch(list1,list2)
>>> list1
['A', 'B', 'E', 'F']
>>> list2
['C', 'D', 'G', 'H']
>>> list3 = ['A', 'B', 'C']
>>> list4 = ['D', 'E', 'F', 'G']
>>> (list3,list4)=StrangeSwitch(list3,list4)
>>> list3
['A', 'B', 'C']
>>> list4
['B', 'C', 'F', 'G']
>>>
Reading the Comments by OP I would take the priviledge of proposing another approach
>>> def StrangeSwitchFast(list1,list2):
#return (list1[:len(list1)/2]+list2[:len(list2)/2],list1[len(list1)/2:]+list2[len(list2)/2:])
return (list(itertools.chain(itertools.islice(list1,0,len(list1)/2),itertools.islice(list2,0,len(list2)/2))),
list(itertools.chain(itertools.islice(list1,len(list1)/2,None),itertools.islice(list2,len(list2)/2,None))))
The above doesn't create any temporary list and if OP desires to use it as an iterator rather than a list for the downstream processing, then the list can be safely dropped from the function and can be left to return as a tuple of iterators.

Categories

Resources