Lists within lists: Even when sliced, there is connection between lists

Asked

Viewed 139 times

5

I’m studying lists and there’s a behavior I don’t understand.

I know that when I equate one list with another, a connection between them is created. And that when I slice a list, I create a copy of the list (no connection between them, that is, when it changes in changes, it does not change in another). However, when there are lists within lists, even with slicing, connection is made between lists.

For example:

>>> a = [[2, 3, 5], [1, 3, 5]]
>>> b = a[:]
>>> print(b)
[[2, 3, 5], [1, 3, 5]]
>>> b[0][1] = 100
>>> print(a[0][1])
100
>>> print(b)
[[2, 100, 5], [1, 3, 5]]
>>> print(a)
[[2, 100, 5], [1, 3, 5]]

I mean, it’s changed on the list b, also changed on the list a. Why did this happen?

  • Did any of the answers solve your question? Do you think you can accept one of them? Check out the [tour] how to do this, if you haven’t already. You would help the community by identifying what was the best solution for you. You can accept only one of them. But you can vote on any question or answer you find useful on the entire site.

3 answers

3

You are using Slice which is nothing more than a reference to the concrete list, just like any list, so if you change something by reference you will be changing in the original list, it’s the same thing internally. The variables are different, but the object they refer to is the same.

If you do not want this you need to make a deep copy where each element is actually copied to a new isolated concrete list. So:

import copy

a = [[2, 3, 5], [1, 3, 5]]
b = copy.deepcopy(a)
b[0][1] = 100
print(a)

Behold working in the ideone. And in the repl it.. Also put on the Github for future reference.

There are other ways to get the same, but what you need to understand is the concept of reference and real copy. Of course, the reference is super fast and has constant time complexity (O(1)) and the real copy is much slower and has linear complexity (O(n)).

An important detail based on other points on this page: Python is not different from other languages, they all work the same. There are authors about Python who sell this idea that it is different, but it is not. In general, those who learn only from these sources believe this. There are differences between Python and other languages, but these concepts are universal. In all languages everything that is state is object, has answer here on the site about this, and variables are always names for memory addresses where the objects are. It may also interest: Everything is object in Python?.

  • Fix: Slices in Python are not references to the concrete list - they are new lists anyway. The problem with AP is that the internal lists, yes, are the same both in the original list and in the copied by Slice. deepcopy solves, of course.

  • Maybe it’s just a matter of nomenclature, I understand what you’re saying, I’ll think of another way to write it more clearly, Slice is a reference, and of course, the list tb, so it got a little confused what I tried to say.

  • No - it’s not nomenclature. Slice is another list - and in fact it’s a way of making an Raza copy of another list. So much so that I have a project (which lacks a polished testing, setup.py) that has a "listview" class that implements Slices as references.

  • https://github.com/jsbueno/extralist/blob/master/extralist/slicedview.py

  • In python, variables are names, but only store references. It is not possible to store anything in a name other than a reference. This is the difference of other languages. In addition all parameter passages are by value, where the value is a reference.

  • @nosklo Variables are names, put a link that shows this. Since they are names in no language you store something in names. You store something in memory where the names are aliases for those addresses. This is a universal concept, no language is different from this. Some languages allow you to place the direct object in that position or by a reference to the object. Other languages, and not only Python, several others allow to have only references. Conceptually does not change anything.

  • @nosklo In essence all languages (have one or another exception, but nothing important) the languages pass arguments (and not parameters, are distinct things) always by value, and in some cases the value is the reference (in Python and other language always is). Python has nothing different, but someone wrote that it has and who does not know the concepts more deeply thinks it is true, but it is not. Of course Python has differences in relation to some languages, but it has nothing special, nothing exclusive.

  • In some languages it is possible to pass the reference, that is, by changing the reference within the function, the name happens to refer to something else outside the function. In python this is not possible, because there is only passage by value (copy), even if the value is a reference, it is copied, that is, it is not possible to change the original reference through the function

  • @nosklo now you’re talking about something else. And in fact it does, only it needs to use a trick :) The only way to prohibit is to have no reference in the language, it is all based on references.

Show 4 more comments

2


A more detailed explanation.

Each list is a separate object. there is no "list a" or "list b". In python, unlike many other languages, variables are only names that if refer to objects.

Let’s go through the code line by line:

a = [[2, 3, 5], [1, 3, 5]]

This line is creating 3 lists. One of the lists has references to two other lists within it. the name a is pointing to one of the 3 created lists, the one containing the other two. To say that this is the "list a" is a simpler way of saying, but in fact this is not the list a. a is only a "name" that "references" the list.

b = a[:]

This line does two things: when using [:] with a list, a new list is created, where all references that were inside the original list are copied. Then a name is created b to refer to that copy.

At this point, you have 4 lists: They are in the order they were created:

PRIMEIRA LISTA:  [2, 3, 5]
SEGUNDA LISTA: [1, 3, 5]
TERCEIRA LISTA: [(PRIMEIRA LISTA), (SEGUNDA LISTA)]
QUARTA LISTA: [(PRIMEIRA LISTA), (SEGUNDA LISTA)]

Note that the content of the third list is the same as the fourth list, ie, the references were copied that point to the first two lists... because that’s what Slice [:] ago, copy references.

So it doesn’t matter if you use a or b to refer to the third or fourth list, because at the end of the processing, when you touch the internal lists, you will be going through the same list!

  • Thanks for the explanation. Yes, I know that everything in Python is object, and if everything is object, because it does not happen the same behavior with numerical variables (by equaling a = 2 and b = a, when change in b , changes in a , and vice versa) ?

  • 2

    @Viniciusviana because you are not "changing in b"... b is just a name, the only thing it can do is refer to objects. What you have to "change" is the object. In the example the object would be the 2 - objects of the type int are not changeable, so it is not possible to modify them. By doing b = b + 1, you will be creating a new object of the type int, the 3, (for b+1 creates a new object) and doing b point to this new created object. O a keeps pointing to the object 2.

  • @Viniciusviana already in the case of lists, for example lista[0] = 10 is effectively changing the list, particularly the first object it refers to (lists are collections of references). lista.append(12) also changes the list. A new list is not created.

  • Thanks, I could understand with this explanation

0

changed in list b, also changed in list a

You nay changed the list b. What you’ve changed is a list that is within of b. This internal list is shared between a and b, because the operation of Slice (slice) makes a Shallow copy.

Behold:

a = [[1, 2, 3], [4, 5, 6]]
b = a[:]

a[0] = [7, 8, 9]

print(a)
print(b)

Note that, although a have changed, b remained identical. However, if you do it here:

a[1][0] = 0

print(a)
print(b)

The amendment will be seen both in a how much in b, because in this second case the change happened on the internal list, and this internal list is the same in a and b.

If you want to copy internal lists as well, use copy.deepcopy in accordance with indicated in that other reply.

  • Thank you for your reply. From what I understand in the python documentation, Slice creates a Shallow copy, which is a new composite object A from the first object B and copies the refocuses (memory locations of objects) of the objects contained in A into B. Deep copy (A deep copy constructs a new compound Object and then, recursively, Inserts copies into it of the Objects found in the original.) creates a new compound object B from A and from each object in A, creates a new object from each object in A, to the deepest level.

  • @Viniciusviana Exactly. An operation of Shallow copy only copies the most external object, already a deep copy copies everything. This concept is not specific to Python, almost every language has this distinction. Shallow copy operations are more common because they are much faster and take up much less memory (since the internal objects are shared).

Browser other questions tagged

You are not signed in. Login or sign up in order to post.