2

I created a function f which uses a 2-dimension list as parameter, but after this function the list does not change at all. As the code below:

def f(t: [[int]]):
    for eachrow in t:
        eachrow = eachrow[1:]
        eachrow.append(0)

A = [[2, 10, 0], [3, 1, 2], [3, 2, 1]]

f(A)

print(A)  # -> [[2, 10, 0], [3, 1, 2], [3, 2, 1]]
wjandrea
  • 28,235
  • 9
  • 60
  • 81

2 Answers2

3

Assigning to eachrow in eachrow = eachrow[1:] overwrites it. So to remove the first element, you could use del instead, or row.pop or slice assignment.

def f(t):
    for row in t:
        del row[0]  # OR row.pop(0) OR row[:] = row[1:]
        row.append(0)

A = [[2, 10, 0], [3, 1, 2], [3, 2, 1]]
f(A)
print(A)  # -> [[10, 0, 0], [1, 2, 0], [2, 1, 0]]
wjandrea
  • 28,235
  • 9
  • 60
  • 81
  • 1
    Technically, it's not that the slice is making a copy, it's that the assignment is happening to a local variable. You would get the same result if the loop body was instead `eachrow = 0`. – wmorrell Nov 15 '19 at 01:47
  • @wmorrell You're right! But why does assigning to a slice work? Is it cause it assigns to the "interior" of the list instead of the name? – wjandrea Nov 15 '19 at 01:51
  • 1
    @wjandrea this is slice-assignment notation: `row[:] = row[1:]`, it isn't the same as "assigning to a slice", that is, it doesn't create a slice (which is just a new list) and assign to that new one, rather, it simply modifies to the original – juanpa.arrivillaga Nov 15 '19 at 02:15
  • 1
    @wmorrell to be clear, `eachrow[1:]` on the right hand side **does** create a slice, i.e. a whole new `list` object – juanpa.arrivillaga Nov 15 '19 at 02:18
  • 1
    Saying a whole new `list` object is a bit misleading. A slice creates a new `slice`, which references the original `list` items. The items themselves are not copied, which is what most people would expect when saying "new `list` object". In other words, `a = row[:]` is different from `a = list(row)`. – wmorrell Nov 15 '19 at 20:12
-1

If you print out the results of your changes to eachrow, you'll see that you ARE updating the eachrow variable, but that doesn't affet the original t variable.

def f(t):
    for eachrow in t:
        eachrow = eachrow[1:]
        eachrow.append(0)
        print(eachrow)
>>> f(A)
[10, 0, 0]
[1, 2, 0]
[2, 1, 0]

If you want to affect the array itself, you should modify the array like so:

def f(t):
    for row_number in range(len(t)):
        t[row_number] = t[row_number][1:]
        t[row_number].append(0)
j6m8
  • 2,261
  • 2
  • 26
  • 34
  • 2
    It's not the iterating over `t` that's the problem, it's the slicing. I posted an answer about this. BTW a list is an iterable, not an iterator since it doesn't get exhausted; and I don't think Python has pointers - a Python name is more like a reference IIRC. But indexing `t` is a perfectly fine way to solve the problem. – wjandrea Nov 15 '19 at 01:20
  • 2
    " the iterator over the elements of that array are COPIES of the element of that list," absolutely incorrect. They are not copies. Put any mutable variable in a list, say, another list: `data = [[], []]` then do `for x in data: x.append(0)` then print `data` and you'll see that the lists inside the outer list have each been modified. – juanpa.arrivillaga Nov 15 '19 at 02:16
  • Noted! Fixed in the answer. – j6m8 Nov 15 '19 at 02:21
  • Thank you to both of you @wjandrea and juanpa for catching this. – j6m8 Nov 15 '19 at 02:22
  • 1
    @j6m8 Whoops, I was wrong actually, it's not the slicing, it's the assignment. wmorrel corrected me and I updated my answer. – wjandrea Nov 15 '19 at 02:26
  • Fair point! I'll update in mine to reflect the same information for the sake of accuracy. – j6m8 Nov 15 '19 at 02:27