Why Can't I Change Only a Single Element in a Nested List in Python

why can't I change only a single element in a nested list in Python

A strange behaviour indeed, but that's only because * operator makes shallow copies, in your case - shallow copies of [0, 0, 0] list. You can use the id() function to make sure that these internal lists are actually the same:

out=[[0]*3]*3
id(out[0])
>>> 140503648365240
id(out[1])
>>> 140503648365240
id(out[2])
>>> 140503648365240

Comprehensions can be used to create different lists as follows:

out = [ [0]*3 for _ in range(3) ]

How do I change an element in a nested list?

Pythonize your loop using:

for i in pointlist:
i[0] = random.randrange(limit1, limit2)
limit1-=100
limit2-=100

This way i becomes an entry of pointlist for easy access

Find and replace an element in a (possibly) nested list in Python?

This does the same as the other answer, just walks the path in a functional fashion:

reduce(getitem, loc[:-1], nested)[loc[-1]] = replace

In Python 3 you'll need to import reduce from functools. And getitem is from the operator module. If you just have lists, you could use list.__getitem__ instead.

Explanation: reduce starts with nested and replaces it with getitem(thecurrentvalue, i) for each value i in loc[:-1]. So for example if loc is [2, 4, 1, 3] then you get getitem(getitem(getitem(nested, 2), 4), 1). Which is the same as nested[2][4][1].

python how to change element in nested list

You have a list of the same [0, 0] element 10 times here:

l=[[0,0]]*10

Any time you modify one, it modifies them all, because they're the same list.

One safe way to make them unique would be:

l = [[0, 0] for _ in range(10)]

One easy way to check would be to print the id of each, which is the memory address where it's stored:

>>> for element in l:
... print id(element)
...
34669128
34669128
34669128
34669128
34669128
34669128
34669128
34669128
34669128
34669128

modifying the elements nested lists in python

The problem comes from how you construct your nested list. The following:

r1=[0 for i in range(3)]
result=[r1 for j in range(3)]

Creates a list of 3 r1. And not a list of 3 different lists which is why, when you modify any of result[j] it modifies r1 and therefore all others result[j]. As pointed out by others, that's the exact same problem as described here, with a different expression: result = [r1] * 3

On the other hand, on your last test you create what you expect, a list of 3 distinct lists:

result = [[0 for x in range(3)] for y in range(3)]

You can do the same using r1:

result = [[x for x in r1] for y in range(3)]

Here the inner "loop" simply copy the elements of r1 into a new (distinct) list:

r1 = [0 for i in range(3)]
result = [[x for x in r1] for y in range(3)]
result[0][0] = 4
print(result)

Prints:

[[4, 0, 0], [0, 0, 0], [0, 0, 0]]

You can observe the original problem in this simpler example where modifications on r1 are also impacting r2:

r1 = [0 for i in range(3)]
r2 = r1
r1[0] = 4
print(r2)

The reason behind this behaviour is simply that r1 and r2 are two names for the same object. When we write r1[0] = 4 we don't change the value of r1 we change the value of the list r1 refers to (or
points to* if you prefer). This is not specific to python and any data structure that can be modified "in-place" will suffer this "problem" (I suggested reading about mutable vs. immutable data structure to better understand what's going on here).

Python: How do I replace value in a nested list?

You are unpacking from the list in for loop and creating 3 variables name, appt and salary. With this variables it isn't possible to easily change your data structure. What you need is extract a list from data and access it by indexing:

from pprint import pprint

data = [['Ben', 'Manager', 3000],
['James', 'Cleaner', 1000],
['Ken', 'Supervisor', 2000]]

for d in data:
if d[0] == 'James':
d[2] = 1500
print(d) # prints only changed row

pprint(data) # prints whole structure

Prints:

['James', 'Cleaner', 1500]
[['Ben', 'Manager', 3000],
['James', 'Cleaner', 1500],
['Ken', 'Supervisor', 2000]]

List of lists changes reflected across sublists unexpectedly

When you write [x]*3 you get, essentially, the list [x, x, x]. That is, a list with 3 references to the same x. When you then modify this single x it is visible via all three references to it:

x = [1] * 4
xs = [x] * 3
print(f"id(x): {id(x)}")
# id(x): 140560897920048
print(
f"id(xs[0]): {id(xs[0])}\n"
f"id(xs[1]): {id(xs[1])}\n"
f"id(xs[2]): {id(xs[2])}"
)
# id(xs[0]): 140560897920048
# id(xs[1]): 140560897920048
# id(xs[2]): 140560897920048

x[0] = 42
print(f"x: {x}")
# x: [42, 1, 1, 1]
print(f"xs: {xs}")
# xs: [[42, 1, 1, 1], [42, 1, 1, 1], [42, 1, 1, 1]]

To fix it, you need to make sure that you create a new list at each position. One way to do it is

[[1]*4 for _ in range(3)]

which will reevaluate [1]*4 each time instead of evaluating it once and making 3 references to 1 list.


You might wonder why * can't make independent objects the way the list comprehension does. That's because the multiplication operator * operates on objects, without seeing expressions. When you use * to multiply [[1] * 4] by 3, * only sees the 1-element list [[1] * 4] evaluates to, not the [[1] * 4 expression text. * has no idea how to make copies of that element, no idea how to reevaluate [[1] * 4], and no idea you even want copies, and in general, there might not even be a way to copy the element.

The only option * has is to make new references to the existing sublist instead of trying to make new sublists. Anything else would be inconsistent or require major redesigning of fundamental language design decisions.

In contrast, a list comprehension reevaluates the element expression on every iteration. [[1] * 4 for n in range(3)] reevaluates [1] * 4 every time for the same reason [x**2 for x in range(3)] reevaluates x**2 every time. Every evaluation of [1] * 4 generates a new list, so the list comprehension does what you wanted.

Incidentally, [1] * 4 also doesn't copy the elements of [1], but that doesn't matter, since integers are immutable. You can't do something like 1.value = 2 and turn a 1 into a 2.

How can I change each element in a nested list in python

To avoid recursion, you need to implement your own stack. Here is an example. (I added a list_d containing your list_c and a scalar, in order to show leaf elements being found at different depths.)

list_a = ["string", 1.0, 1.0]
list_b = [1.0, "string", 1.0]
list_c = [list_a, list_b]
list_d = [2.0, list_c]

def walk_list(l):

if not l:
return

parent_lists = []
parent_indices = []

current_list = l
current_index = 0

while True:

# descend from current position through lists until
# leaf element or empty list is reached
while True:
val = current_list[current_index]
if not isinstance(val, list):
# found leaf element (result of interest)
yield (val,
parent_lists + [current_list],
parent_indices + [current_index])
break
elif not val:
# found empty list (ignore)
break
else:
# list to descend into
parent_indices.append(current_index)
parent_lists.append(current_list)
current_list = val
current_index = 0

# increment to next position, reascending to parent
# each time we go off the end of any list
while True:
current_index += 1
if current_index < len(current_list):
break
if not parent_lists:
return
current_list = parent_lists.pop()
current_index = parent_indices.pop()

print('Original: ', list_d)
print()

for val, lists, indices in walk_list(list_d):
print("saw value {} at position {}".format(repr(val), indices))
if isinstance(val, float):
lists[-1][indices[-1]] /= 2

print('\nFinal: ', list_d)

gives:

Original:  [2.0, [['string', 1.0, 1.0], [1.0, 'string', 1.0]]]

saw value 2.0 at position [0]
saw value 'string' at position [1, 0, 0]
saw value 1.0 at position [1, 0, 1]
saw value 1.0 at position [1, 0, 2]
saw value 1.0 at position [1, 1, 0]
saw value 'string' at position [1, 1, 1]
saw value 1.0 at position [1, 1, 2]

Final: [1.0, [['string', 0.5, 0.5], [0.5, 'string', 0.5]]]

The lists that comes back from the iterator is a list of increasingly deep sublists which are encountered in the path from the original list to the element in question, i.e. lists[0] will contain the original list and lists[-1] is the immediate parent list containing the value concerned.

Obviously if modifying the list while iterating over it, you need to be careful to only modify scalar values, not to modify its structure by appending or removing values from any list objects.

replacing items in nested lists python

What you're doing here is a shallow copy of the list, so when you change the copy, the original changes as well. What you need is deepcopy

import copy
order = [['yhjK', 'F'], 'gap', ['bcsA', 'F'], ['bcsB', 'F'], ['bcsZ', 'F'],
'gap', ['yhjK', 'R']]
order_1 = copy.deepcopy(order)

# Now changing order_1 will not change order
order_1[1] = ['TEST LIST']
print order[1] # Prints 'gap'
print order_1[1] # Prints '['TEST LIST']

Python: Changing values of a nested list

I think the reason for this can be demonstrated on this very simple example:

a = [1,2,3]

b = [a,a,a] # <- this is what you are actually doing

print(list(map(id,b)))
#[140475353832712, 140475353832712, 140475353832712]

Please not that all ids are same above. This means that you are stroning in your list, references to the same object. thus changing one subsist, changes every sublist.

However, you should be doing this (or some equivalent):

b2 = [a[:], a[:], a[:]] #
print(list(map(id,b2)))
#[140475353832136, 140475353832072, 140475353832520]

Please note, different id for each sublist. Now b2 contains references to different sublists.

To sum up, you should have this:

for y in range(attributes[1]):
self.board.append(rows[:])


Related Topics



Leave a reply



Submit