#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Oct 2012
    Posts
    1
    Rep Power
    0

    List element referencing


    Hello, I'm in need of some help in understanding referencing in lists. I was wondering if I could get

    >>> test = []
    >>> b = [' ', ' ', ' ', ' ']
    >>> test = [b[0], b[3]]
    >>> b[3] = 'X'
    >>> test
    [' ', ' ']

    to instead have test = [' ','X']?
  2. #2
  3. Commie Mutant Traitor
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Jun 2004
    Location
    Alpharetta, GA
    Posts
    1,806
    Rep Power
    1570
    The problem here is that the assignment
    Code:
    test = [b[0], b[3]]
    does not create a persistent reference to b[0] or b[3] (which isn't there yet, anyway); rather it creates a new list containing copies of the values that were in b[0] and b[3], both of which were empty strings (b[0] explicitly, b[3] by default). Changing the values in the original has no effect on the copy.
    Rev First Speaker Schol-R-LEA;2 JAM LCF ELF KoR KCO BiWM TGIF
    #define KINSEY (rand() % 7) λ Scheme is the Red Pill
    Scheme in Short Understanding the C/C++ Preprocessor
    Taming Python A Highly Opinionated Review of Programming Languages for the Novice, v1.1

    FOR SALE: One ShapeSystem 2300 CMD, extensively modified for human use. Includes s/w for anthro, transgender, sex-appeal enhance, & Gillian Anderson and Jason D. Poit clone forms. Some wear. $4500 obo. tverres@et.ins.gov
  4. #3
  5. No Profile Picture
    Contributing User
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Feb 2004
    Location
    San Francisco Bay
    Posts
    1,939
    Rep Power
    1313
    Originally Posted by Schol-R-LEA
    The problem here is that the assignment
    Code:
    test = [b[0], b[3]]
    does not create a persistent reference to b[0] or b[3] (which isn't there yet, anyway); rather it creates a new list containing copies of the values that were in b[0] and b[3], both of which were empty strings (b[0] explicitly, b[3] by default). Changing the values in the original has no effect on the copy.
    I'll elaborate on Schol-R-LEA's reply, since a beginner might take from this that Python is doing a deep copy here, which is not the case. After this:
    Code:
    test = [b[0], b[3]]
    test[0] is identical to b[0], and test[1] is identical to b[1]. Let's not use strings as list elements here to avoid the issue of interning, which would only make it confusing. Let's use tuples:
    Code:
    >>> b = [(0,1),(2,3),(4,5),(6,7)]
    >>> test = [b[0], b[3]]
    >>> id(b[0])
    3070077420
    >>> id(b[3])
    3070077196
    >>> id(test[0])
    3070077420
    >>> id(test[1])
    3070077196
    The id builtin tells you the "identity" of an object. Intuitively, two objects are identical (i.e., they have the same identity) if their data is literally stored in the same bytes of memory. As we can see, test[1] isn't a copy of b[3]; it's referencing literally the same block of memory. (Note: if you try this example, you won't have the same numbers, but the ones that are the same will be the same, etc.)

    But now let's change an element of b:
    Code:
    >>> b[3] = (8,9)
    >>> b
    [(0, 1), (2, 3), (4, 5), (8, 9)]
    >>> test
    [(0, 1), (6, 7)]
    Even though b[3] and test[1] were referencing exactly the same object, "changing" b[3] didn't affect test[1]. How can that be? The answer is right here:
    Code:
    >>> id(b[3])
    3070077612
    >>> id(test[1])
    3070077196
    The key is that the assignment statement
    Code:
    b[3] = (8,9)
    doesn't go and modify the object that b[3] is pointing to; it just makes b[3] refer to a new object. test[1], of course, is still referring to the old object. Actually, there's no way to modify the actual blocks of memory holding either b[3] or test[1], since tuples are immutable. (Relevant to the OP: so are strings.) This means that there is absolutely no way to mess around with the list `b' and have it change the value of test[1]: it will always be the tuple (6, 7) because there is no operation on a tuple that changes the data it contains.

    If you want two lists to share data in such a way that modifying one list will affect the other one, you need to: (1) store a mutable object in the list, and (2) mutate that object via whatever operations are defined on that object. For example, lists are obviously mutable (we mutated `b' above):
    Code:
    >>> b = [[0,1],[2,3],[4,5],[6,7]]
    >>> test = [b[0], b[3]]
    >>> b[3][:] = [8,9]
    >>> b
    [[0, 1], [2, 3], [4, 5], [8, 9]]
    >>> test
    [[0, 1], [8, 9]]
    This:
    Code:
    b[3][:] = [8,9]
    is equivalent to:
    Code:
    list.__setitem__(b[3], slice(None, None, None), [8,9])
    which modifies the list referenced by b[3], since that's what list.__setitem__ does. Since test[1] refers to the same object, modifying b[3] also modifies test[1].

    However, to contrast:
    Code:
    >>> b = [[0,1],[2,3],[4,5],[6,7]]
    >>> test = [b[0], b[3]]
    >>> b[3] = [8,9]
    >>> test
    [[0, 1], [6, 7]]
    This line:
    Code:
    b[3] = [8,9]
    is equivalent to:
    Code:
    list.__setitem__(b, 3, [8,9])
    which does not modify the object b[3] (which might even be an immutable object as far is this particular function call is concerned); it modifies the object `b', making b[3] reference a new object [8,9] and leaving the value of test[1] unmodified.

    Finally, returning to the OP, as I hinted at above, there is no way to do literally what you said: modify `b' and have it change test to [' ', 'X']. However, here's a very easy workaround:
    Code:
    >>> b = [[' '], [' '], [' '], [' ']]
    >>> test = [b[0], b[3]]
    >>> b[3][0] = 'X'
    >>> test
    [[' '], ['X']]
    It's good to understand all of this to avoid being bitten down the road, for example, when you modify an object and unwittingly affect some distant part of your program because you forgot that you were mutating an object with multiple references to it.

IMN logo majestic logo threadwatch logo seochat tools logo