This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 6 years ago.
I some lines of code in order to prep the data to be sorted. In order to do this, I needed each item in the list to be a list. For example, if my list was [a, b, c, d], the list should be become [[a], [b], [c], [d]] where a, b, c, and d are integers. My code however returns [[d], [d], [d], [d]]:
len_of_list = random.randint(8500, 10000)
val_list = range(len_of_list)
list_one = [[-1]]
for index in range(0, len_of_list):
list_one[0][0] = val_list[index]
val_list[index] = list_one[0]
print val_list
The weirdest thing that happens to the code is that when the second-to-last line is replaced with val_list[index] = list_one[0][0], it returns the correct values just without the []. Thus I assumed that removing the last [0] would return the values with the [] around them. But what is returned was the last integer in the list originally surrounded by [] for all values. This shouldn't happen because list[0][0] is reset every iteration with list[0][0] = val_list[index].
So why is the list being returned as [[d], [d], [d], [d]] instead of [[a], [b], [c], [d]]. Of course, the list has at least 8500 unique integers but d represents the last value in the list or put simply d = val_list[len(val_list) - 1].
You put list_one[0] in val_list[index]. Since list_one[0] is a list, val_list[index] receives its address, not its value. Therefore, when the value of list_one[0] is changed, ie. at every iteration, the value of val_list[index] for every index is changed as well.
Thus, the final value contained at every index of the list is the last one, [d].
This happens because list_one[0] is a list, which is mutable.
Beyond this fact, your code is not really pythonic. First, you build an empty list of length len_of_list by writing val_list = range(len_of_list). While this works in Python 2, it does not in Python 3, because a range object is semantically different from a list. Besides, a list should be built by appending or inserting elements, but not by making a long enough empty list and then filling the indices.
Then, you are using complicated code to build your list of list, while the following would have been enough:
for index in range(len_of_list):
val_list[index] = [index]
A more basic pythonic way to do the same, with append, would be:
len_of_list = random.randint(8500, 10000)
val_list = list()
for index in range(len_of_list):
val_list.append([index])
However, the most pythonic way would be to use a list comprehension. If you have l = [a, b, c, d], and you want to have [[a], [b], [c], [d]], the fastes is the following:
[[elt] for elt in l]
Try this:
[[x] for x in myList]
Related
In a general case I'm classifying data points in nested lists with variable depths of nested loops, for example in a simple case :
alist = [ [[a, b], [c, d]], [[e, f], [g, h]] ]
I use this to have operations like brackets, for example :
min ([ max([a, b]), max([c,d]) ])
However the problem I encounter is that in my example I reference [a, b] and [c, d], but I want to reference these as variables or indexes of a list in the case were we have a known depth of nested lists and known number of elements in the deepest nested brackets.
With what I know about using list indexes, I don't see how I can reference a nth depth in a nested list. If I wanted to reference the third nested list I have to explicitly write:
nlist[0][0][i]
And therefore if the depth varies, I can't do anything.
You'll need to know more than just the depth. Like in your final example, you need to have 3 values: 0, 0, i.
In the generic case you would need to know n indices.
So you could write a little helper function that takes those indices as argument:
def deep_get(lst, *indices):
for i in indices:
lst = lst[i]
return lst
And now when you have a list indices you can do:
indices = [0, 0, i]
# ...
# ...
print(deep_get(lst, *indices))
Setting
If you need to set a value instead of getting it, then use this function:
def deep_set(lst, value, *indices):
for i in indices[:-1]:
lst = lst[i]
lst[indices[-1]] = value
Call as:
indices = [0, 0, i]
# ...
# ...
newValue = 9
print(deep_set(lst, newValue, *indices))
I have a list of lists
input = [[2,13],[5,3],[10,8],[13,4],[15,0],[17,10],[20,5],[25,9],[28,7],[31,0]]
I want to write a list comprehension where for the [a,b] pairs above I get the pairs where b > a. In the above example that would be [2,13].
My attempt
x = [[item[i],[j]] for item in inputArray if j>i]
produces a NameError
NameError: name 'j' is not defined`
The problem with your attempt is that you never tell Python what i and j are supposed to be. The check j > i cannot be computed and the list [item[i],[j]] can't be built without that information.
You can issue
>>> inp = [[2,13],[5,3],[10,8],[13,4],[15,0],[17,10],[20,5],[25,9],[28,7],[31,0]]
>>> [[a, b] for a, b in inp if b > a]
[[2, 13]]
This solution does not produce a NameError because for a, b in inp tells Python to iterate over the elements of inp (two-element sublists) and in each iteration assign the name a to the first element of a sublist and the name b to the second element.
I used the name inp instead of input because the latter is already taken by a builtin function for getting user input.
Explanation of the list comprehension
The comprehension is equivalent to
>>> result = []
>>> for a, b in inp:
... if b > a:
... result.append([a, b])
...
>>> result
[[2, 13]]
Every two-element list in inp is unpacked into the variables a and b. If the filter condition b > a is True, then a list [a, b] is built and included in the final result.
If you don't want to use unpacking, you can also index into the sublists of inp like this:
>>> [sub[:] for sub in inp if sub[1] > sub[0]]
[[2, 13]]
Taking a full slice of sub via sub[:] ensures that like in the other solutions presented so far, the filtered result stores (shallow) copies of the sublists of inp. If copying it not necessary, you can omit the [:].
This code does not produce a NameError because for sub in inp tells Python to iterate over inp and in each iteration assign the name sub to the next sublist. In addition, explicit numbers (0 and 1) are used for the indices.
Personally, I prefer the solution with unpacking. It is easier to read and will run even if the elements of inp don't support indexing, but are iterables from which two elements can be extracted.
You should unpack each pair into the i, j variables, and then you can compare them:
x = [[i, j] for i,j in inputList if j > i]
(note I have renamed inputArray, inputList)
Or without unpacking:
x = [item for item in inputList if item[1] > item[0]]
Let's say I have:
a = 1
b = 2
C = 'r'
my_list = [a,b,c]
Now let's say that a, b and c are unknown and I don't know their names.
If I do:
for x in my_list: del x
it doesn't work. a, b, c have not been deleted.
Can someone explain me why?
As #Coldspeed mentions in his comment, the variable x which you delete is not the same as the element in the list object.
Similar behaviour will be seen if you try to assign to x:
for x in my_list: x='bla' #does not modify anything in my_list
However, as the items are references to the same memory block, the comparison x is my_list[0] will equate to True in the first loop iteration.
As such, it is possible to perform operations on the list through usage of the shared reference, for example:
for x in my_list[:]: my_list.remove(x) #results in an empty list
Care has to be taken to first create a copy of the list and iterate over these items though, as was done in the previous lines. If you are hasty and loop over the items of a dynamically changing list, you will run into some more python magic.
for x in my_list: my_list.remove(x) #the first element gets deleted, then the second element in the list, which now has length 2, is deleted.
#Final result is the list [2] remaining
you have multiple issues here:
1. variable in list
a = 1
b = 2
my_list = [a,b]
assigns the values 1 and 2 to the list, not the vars. You can use mutable objects to get you desire: Immutable vs Mutable types
2. deleting a copy from a listvalue
for x in my_list:
del x
like in 1. x is just the value from the list (e.g. 1, 2, 'c'), but even worse, its a additional reference count to the memory.
Deleting it results in decreasing the counter, not deleting the value from memory, since at least one more counter is given by the original list (and in your case the vars (a,b,c) from the beginning).
More Info: Arguments are passed by assignment
3. deleting while iterating
for x in my_list:
del x
contains an other problem. If you would change the code to mylist.remove(x), to at least remove the entrie from the list, you would also skip every second member of the list. Quick Example:
li = [1,2,3]
for x in li:
li.remove(x)
first iteration would be x = 1. Deleting 1 from li results in li = [2,3]. Then the loop continous with the second position in the list: x=3 and deleting it. 2 was skipped.
This can be avoided by using a copy of the list using the [:] operator:
for x in li[:]:
li.remove(x)
This finaly results in an empty list
I have a list and I am trying to delete the elements that have 'pie' in them. This is what I've done:
['applepie','orangepie', 'turkeycake']
for i in range(len(list)):
if "pie" in list[i]:
del list[i]
I keep getting list index out of range, but when I change the del to a print statement it prints out the elements fine.
Instead of removing an item from the list you're iterating over, try creating a new list with Python's nice list comprehension syntax:
foods = ['applepie','orangepie', 'turkeycake']
pieless_foods = [f for f in foods if 'pie' not in f]
Deleting an element during iteration, changes the size, causing IndexError.
You can rewrite your code as (using List Comprehension)
L = [e for e in L if "pie" not in e]
Something like:
stuff = ['applepie','orangepie', 'turkeycake']
stuff = [item for item in stuff if not item.endswith('pie')]
Modifying an object that you're iterating over should be considered a no-go.
The reason to why you get a error is because you change the length of the list when you delete something!
Example:
first loop: i = 0, length of list will become 1 less because you delete "applepie" (length is now 2)
second loop: i = 1, length of list will now become just 1 because we delete "orangepie"
last/third loop: i = 2, Now you should see the problem, since i = 2 and the length of the list is only 1 (to clarify only list[0] have something in it!).
So rather use something like:
for item in in list:
if "pie" not in item:
new list.append(item)
Another but longer way would be to note down the indexes where you encounter pie and delete those elements after the first for loop
I want to get the union of 2 nested lists plus an index to the common values.
I have two lists like A = [[1,2,3],[4,5,6],[7,8,9]] and B = [[1,2,3,4],[3,3,5,7]] but the length of each list is about 100 000. To A belongs an index vector with len(A): I = [2,3,4]
What I want is to find all sublists in B where the first 3 elements are equal to a sublist in A. In this example I want to get B[0] returned ([1,2,3,4]) because its first three elements are equal to A[0]. In addition, I also want the index to A[0] in this example, that is I[0].
I tried different things, but nothing worked so far :(
First I tried this:
Common = []
for i in range(len(B)):
if B[i][:3] in A:
id = [I[x] for x,y in enumerate(A) if y == B[i][:3]][0]
ctdCommon.append([int(id)] + B[i])
But that takes ages, or never finishes
Then I transformed A and B into sets and took the union from both, which was very quick, but then I don't know how to get the corresponding indices
Does anyone have an idea?
Create an auxiliary dict (work is O(len(A)) -- assuming the first three items of a sublist in A uniquely identify it (otherwise you need a dict of lists):
aud = dict((tuple(a[:3]), i) for i, a in enumerate(A))
Use said dict to loop once on B (work is O(len(B))) to get B sublists and A indices:
result = [(b, aud[tuple(b[:3])]) for b in B if tuple(b[:3]) in aud]