How do I copy a list in an iteration? - python

I need to make a copy of a list from a list of lists. The following code gives an error message:
y = list[x]
TypeError: unsubscriptable object
a = [[0],[1]]
for x in a:
y = list[x]
print y
What am I doing wrong here?

y = list[x]
Are you sure you aren't meaning to call the list constructor with the variable x as a parameter, instead of trying to access the element 'x' in the variable 'list'?
As in:
y = list(x)

y=list(x)
The above one should work fine

list is actually the type, so it makes no sense to try and get its x element. If you want to instantiate a list, you need to use list(iterable).
Now, if you want to copy a list, the simpler solution would be to use the copy module.
import copy
a = [[0],[1]]
new_list = copy.copy(a[0])
Pay attention to the fact that if you want to copy an item with nested elements, you'll have to use copy.deepcopy.

Related

How to replace specific value in list?

I´ve the following list:
x = [['0_1 1_0']]
I want to replace 0_1 with 0.
This is what ive already tried:
x[0].replace("0_1", "0")
But then I get the following erorr:
AttributeError: 'list' object has no attribute 'replace'
It should be:
x[0][0] = x[0][0].replace("0_1", "0")
Since:
>>> x = [['0_1 1_0']]
>>> x
[['0_1 1_0']]
>>> x[0]
['0_1 1_0']
>>> x[0][0]
'0_1 1_0'
>>> x[0][0] = x[0][0].replace("0_1", "0")
>>> x
[['0 1_0']]
And since strings are immutable, you can't change them in-place, but have to reassign to it.
Since you have a 2D list when you do x[0] you access the list within the list. So do x[0][0].replace("0_1", "0")
So you need to understand the eco system that where you can use replace function. For example if there is a string I want to replace string operation properly and you want to replace the replace by learn the that's how it works.
string = "I want to replace string operation properly"
new_string = string.replace("replace", "learn")
In your case the string item is inside of a nested list. So first you need to access to the item in the nested list:
test_list = [["foo_1"], ["foo_1"], ["bar_2"], ["soo_2"]]
new_list = []
for item in test_list: # iterate through the list and fix it
print("Actual:", item[0])
print("Replaced:", item[0].replace("foo", "who"))
new_list.append(item[0].replace("foo", "who"))
you have defined a list into a list, therefore, you need to provide both indexes to access the information that in your case is written as a block '0_1 1_0'. As a consequence, you have to change the whole block, not only the "0_1" that you want.
x[0][0] = '0' #--> will generate x = [['0']].
Another option is to define a list, element by element:
x = ['0_1','1_0']
Then, you can replace the content by providing the respective index
x[0] = '0'
I hope it works.
Best regards
Ramir

Why do these two forms of iteration produce different results?

alist = ['1', '2', '3']
blist = alist[:]
for x in alist:
if not "#gmail.com" in alist:
x = x + "#gmail.com"
for x in range(len(blist)):
if not "#gmail.com" in blist[x]:
blist[x] = blist[x] + "#gmail.com"
The first block of code does not implement what I need, yet the second one does.
What is the difference between these two blocks of code?
When you do x = x + "#gmail.com" in the first version of the code, you're creating a new value and rebinding the name x to refer to it. This has no effect on alist, even though that's where the previous x value came from.
When you do blist[x] = blist[x] + "#gmail.com" on the other hand, you're explicitly modifying the list. You're rebinding blist[x] to refer to a new value.
Note that with different list contents, you might have been able to make the first version of the code work, using "in place" modification. Strings are immutable though, so there are no in-place operations. However, if alist contained mutable items such as lists, code like x += ["foo"] would extend the inner list in place. The += operator will attempt to do an in place addition if the type of the object supports it (by having an __iadd__ method). For types that don't support inplace operations though, it's just the same as x = x + y, which will have the same issue you've encountered.
Shorty: In first sample you aren't mutating list values, in second example you do.
In first loop you modifying x, which actually is copy of list item, but not that item, in second sample you modifying item of list by accessing it by index. See Immutable vs Mutable types to get more info regarding mutable and immutable types
However if you would apply loops on list which contain mutable types, the items would be modified by both for loops:
alist = [['1'], ['2'], ['3']]
blist = [['1'], ['2'], ['3']]
for x in alist:
x.append("#gmail.com")
print alist
for x in range(len(blist)):
blist[x].append("#gmail.com")
print blist
Your fist loop can be fixed to be:
for i, x in enumerate(alist):
if "#gmail.com" not in x:
alist[i] = x + "#gmail.com"
Or, more succinctly:
email="#gmail.com"
alist=[x+email if email not in x else x for x in alist]
Since in will return true if that string is anywhere in the string, not x.endswith("#gmail.com") is preferred in both cases above.
Because strings are immutable:
https://docs.python.org/2/reference/datamodel.html
Objects whose value can change are said to be mutable; objects whose
value is unchangeable once they are created are called immutable. (The
value of an immutable container object that contains a reference to a
mutable object can change when the latter’s value is changed; however
the container is still considered immutable, because the collection of
objects it contains cannot be changed. So, immutability is not
strictly the same as having an unchangeable value, it is more subtle.)
An object’s mutability is determined by its type; for instance,
numbers, strings and tuples are immutable, while dictionaries and
lists are mutable.
In the first example, you are creating a new variable named x each time you concatenate additional values to it.
I the second example, however, you are simply changing the value at the same index of your list.

How to modify values in list of lists

I have written the python code in the following form
temp=[]
x=[1,2,3]
for i in range(4):
temp=temp+[x]
temp[1][1]=x[1]+1
print temp[0]
print temp[1]
Here, I wanted the value of just temp[1][1], but the value of temp[0][1] also gets changed. Is there a way of changing just one value? I created a new list and tried to add it to temp, but that does not seem to work as well.
Update:
Thanks, but it did not seem to work in my case (which was a multi dimensional array). I have the code has follows:
tempList=[]
for i in range(openList[0].hx):
tempList=tempList+[copy.copy(abc)]
tempList[0][0][0]=123
print sudokuList
Here abc is a two dimensional list. Modifying the value of tempList[0][0][0] changes the value of tempList[1][0][0] and so on.
That's because of that you are assigning the x to all of your list items so all of them are references to one object and once you change one on them actually you have changed all of them. for getting ride of this problem you can use a list comprehension to define the temp list :
temp=[[1,2,3] for _ in range(4)]
temp[1][1]=7
print temp[0]
print temp[1]
result :
[1, 2, 3]
[1, 7, 3]
This is actually a common error for beginners to Python: How to clone or copy a list?
When you add x to temp four times, you're creating a temp which has the same x four number of times.
So, temp[0], temp[2], temp[3] and temp[4] are all pointing to the same x you declared at the first line.
Just make a copy when adding:
temp=[]
x=[1,2,3]
for i in range(4):
temp=temp.append(x[:])
temp[1][1]=x[1]+1
print temp[0]
print temp[1]
You can see it with id function, which returns a different value for different objects:
>>> temp=[]
>>> x=[1,2,3]
>>> for i in range(4):
... temp=temp+[x]
...
>>> id(temp[0]), id(temp[1])
(4301992880, 4301992880) # they're the same
>>> temp=[]
>>> x=[1,2,3]
>>> for i in range(4):
... temp=temp+[x[:]]
...
>>> id(temp[0]), id(temp[1])
(4301992088, 4302183024) # now they are not
Try, the following. x in for loop is a reference to the original x and not a copy. Because of this reference, changing any element reflects on all objects. So you would need to make a copy as used in following snippet.
temp=[]
x=[1,2,3]
for i in range(4):
temp=temp+[x[:]]
temp[1][1]=x[1]+1
print temp[0]
print temp[1]
----EDIT----
As per your comment, use copy.deepcopy to copy the list. deepcopy would recursively copy all the referenced elements inside the list. Check copy.deepcopy. So the code looks like:-
import copy
temp=[]
x=[1,2,3]
for i in range(4):
x_copy = copy.deepcopy(x)
#do something with x_copy. use this inplace of x in your code.
#will work for 1D or 2D or any other higher order lists.

Delete a specific value from multi-value dictionary in python

``I have a dictionary in python like this.
dictionary = {"00":[1,2,3,4,5,6,7,8,9],"01":[1,2,3,4,5,6,7,8,9],"02":[1,2,3,4,5,6,7,8,9],"03":[1,2,3,4,5,6,7,8,9],"04":[1,2,3,4,5,6,7,8,9]........up-to "99":[1,2,3,4,5,6,7,8,9]}
I have to delete the value 2 from the list of "00".I tried it using following statement.
del (dictionary[key][dictionary[key].index(sudokumatrix[i][iindex])]).
Here key has value "00" and sudokumatrix[i][iindex] has value 2.But i got resulting dictionary as
{"00":[1,3,4,5,6,7,8,9],"01":[1,3,4,5,6,7,8,9],"02":[1,3,4,5,6,7,8,9],"03":[1,3,4,5,6,7,8,9],"04":[1,3,4,5,6,7,8,9].....}.
I have to get the result as:
{"00":[1,3,4,5,6,7,8,9],"01":[1,2,3,4,5,6,7,8,9],"02":[1,2,3,4,5,6,7,8,9],"03":[1,2,3,4,5,6,7,8,9],"04":[1,2,3,4,5,6,7,8,9]....}
I am posting the whole code here:
dictionary = dict()
zerotonine = "123456789"
list2 = list(zerotonine)
list2 = [int(i) for i in list2]
sudokumatrix=[]
for p in range(9):
for q in range(9):
keyis=str(p)+str(q)
dictionary[keyis] = list2
for i in range(9):
initialinput = [1,2,3,4,5,6,7,8,9]
list1=list(initialinput)
list1 = [int(i) for i in list1]
sudokumatrix.append(list1)
key = "00"
del dictionary[key][dictionary[key].index(sudokumatrix[0][1])]
print dictionary
EDIT == I guess(since the generation of dictionary is not given) ==EDIT.
The reason is that the values of keys '00', '01', ... are pointing to the same list. Modifying one of them will definitely affect the others.
Try using this to generate your dict
dictionary = dict((str(x).zfill(2), range(1, 10)) for x in range(100))
Your code of this part is actually not wrong, but to use list.remove() will make it much better.
The issue has to do with pointers.
replace this:
dictionary[keyis] = list2
with this:
dictionary[keyis] = [int(i) for i in list2]
You're creating list2 correctly, but when you go into the loop Python doesn't make a brand new copy of it with every iteration. It makes a pointer to the original list. Python sees:
dictionary[keyis] = list2
and says "oh, list2? I recognize that name! I have that as an object in memory already! I'll save some space by just updating the original copy and linking it here! Any time someone wants to view it or update it I'll just deal with the original and everything will be awesome forever!!!"
OK, so maybe the python interpreter isn't that enthusiastic, but that's how I like to think of it. The end result is that all of your dictionary values are pointing at the original list.
If you don't mind deleting every occurrence of 2 in the list, you can use list comprehension:
dictionary["00"] = [i for i in dictionary["00"] if i != 2]
This will create a new list, and will avoid altering the other values, as it appears all your dictionary values reference the same list.
EDIT: Yep your dictionary values reference the same list
you could use dictionary and list comprehension to create your dictionary
dictionary = {str(x):[i for i in range(10)] for x in range(100)}

Accessing elements of a list

I have a list of strings, and calling a function on each string which returns a string. The thing I want is to update the string in the list. How can I do that?
for i in list:
func(i)
The function func() returns a string. i want to update the list with this string. How can it be done?
If you need to update your list in place (not create a new list to replace it), you'll need to get indexes that corresponds to each item you get from your loop. The easiest way to do that is to use the built-in enumerate function:
for index, item in enumerate(lst):
lst[index] = func(item)
You can reconstruct the list with list comprehension like this
list_of_strings = [func(str_obj) for str_obj in list_of_strings]
Or, you can use the builtin map function like this
list_of_strings = map(func, list_of_strings)
Note : If you are using Python 3.x, then you need to convert the map object to a list, explicitly, like this
list_of_strings = list(map(func, list_of_strings))
Note 1: You don't have to worry about the old list and its memory. When you make the variable list_of_strings refer a new list by assigning to it, the reference count of the old list reduces by 1. And when the reference count drops to 0, it will be automatically garbage collected.
First, don't call your lists list (that's the built-in list constructor).
The most Pythonic way of doing what you want is a list comprehension:
lst = [func(i) for i in lst]
or you can create a new list:
lst2 = []
for i in lst:
lst2.append(func(i))
and you can even mutate the list in place
for n, i in enumerate(lst):
lst[n] = func(i)
Note: most programmers will be confused by calling the list item i in the loop above since i is normally used as a loop index counter, I'm just using it here for consistency.
You should get used to the first version though, it's much easier to understand when you come back to the code six months from now.
Later you might also want to use a generator...
g = (func(i) for i in lst)
lst = list(g)
You can use map() to do that.
map(func, list)

Categories

Resources