1
class MyClass:
    def __init__(self, a):
        self.a = a

def append(some_list):
    some_list.append(["d"])
    
foo =[["a"],["b"],["c"]]
bar = foo
my_class = MyClass(foo)

append(bar)

for item in bar:
    item[0] += "x"
    letters = item[0]
    letters += "z"
print (my_class.a)

Yields the output

[['ax'], ['bx'], ['cx'], ['dx']]

There’s quite a bit going on in the example and I feel pretty good about all of it except for I’d expect “z” to be tagged onto the strings as well but it’s not.

Can someone explain why it makes sense “z” is not included on the strings?

I thought that the indexing would return “the container” with the string and then appending the “z” would alter the stored string. Apparently a distinct “container” is made but I don’t understand how or why.

(If appending to the string makes a new string, I don’t know why the same behavior would happen with integers as well which I tested... Would using floats have a different result?)

financial_physician
  • 1,672
  • 1
  • 14
  • 34
  • 1
    Does this answer your question? [List changes unexpectedly after assignment. How do I clone or copy it to prevent this?](https://stackoverflow.com/questions/2612802/list-changes-unexpectedly-after-assignment-how-do-i-clone-or-copy-it-to-prevent) – deadshot Aug 05 '20 at 03:32
  • @deadshot it does not because it doesn’t answer why `letters = item[0]` and `bar = foo` behave differently —one modifies the variable it’s derived from and the other does not. Why are the containers being pointed to identical for one and not for the other? Does `item[0]` create a new spot in memory? Why would it do that their but not for `bar = foo`? – financial_physician Aug 05 '20 at 03:56
  • @financial_physician you really should read the following: https://nedbatchelder.com/text/names.html. In any case, those assignment statements don't behave differently. However, in one case, you are mutating a list, in the other, you are creating a new string (which is what the `+` operator does with stings). – juanpa.arrivillaga Aug 05 '20 at 05:07
  • @juanpa.arrivillaga This is a good article. I think it explains the AerysS article. The quick paragraph about mutable vs immutable objects is where my gap in knoweldge was. Both articles made the distinction clear. – financial_physician Aug 05 '20 at 06:17

1 Answers1

1

It is because of mutable object. For example

>>>l1 = [1,2,3]
>>>l2 = l1
>>>l2[0]=4
>>>print(l1[0])
4

In the example, l1 and l2 points to the same memory location. To create l1 as a copy of l2, use copy():

>>>l1 = [1,2,3]
>>>l2 = l1.copy()
>>>l2[0]=4
>>>print(l1[0]) 
1
>>>print(l2[0])
4

EDIT: change explanation as a comment suggested.

In your example, foo and bar point to the same memory location: what you changes in foo also changes in bar. Why it does not append "z" is because letters is a declared variable that has the value of item[0], but it is not mutating. If you print(letters) it will print "dz".

Minh-Long Luu
  • 2,393
  • 1
  • 17
  • 39
  • Okay, so what’s the difference between a declared variable and declaring a variable as being equal to another one? It seems like you’re declaring variables in both of them so I’m having a hard time figuring out why it should be a distinct container when declaring `letters = item[0]` but not when declaring `bar = foo` – financial_physician Aug 05 '20 at 03:54
  • 2
    `bar` and `foo` are mutable objects, while `letters=item[0]` is not, it just how it works: https://medium.com/@meghamohan/mutable-and-immutable-side-of-python-c2145cf72747 – Minh-Long Luu Aug 05 '20 at 04:28
  • Works for me. Thank you! – financial_physician Aug 05 '20 at 04:32
  • 1
    @AerysS your explanation is incorrect. "because `letters` is a declared variable that has the value of `item[0]`, but it does NOT point to that memory address." Yes, it does, it refers to the same object (talking about memory addresses is not really helpful in Python, but if we are, then they point to the same address). In Both examples, the variable refers **to the same object** as what is in the list, but in the case of the list, you are mutating the object, i.e. `item[0] += "x"` while in the second case, you are not mutating the string, i.e. `letters += "z"`. – juanpa.arrivillaga Aug 05 '20 at 05:03
  • 1
    @financial_physician it isn't, this explanation is wrong. The distinction is simple, `list` objects expose mutator methods, ie. they are mutable. `item[0] += "x"` *mutatues* the list being referred to by `item`, which is the same list in `bar`. On the other hand, `letters += "z"` *does not mutate the string object being referred to by `letters`*, string objects doesn't expose mutator methods (they are immutable). Even though, `letters` is the same list object as `item[0]` – juanpa.arrivillaga Aug 05 '20 at 05:04
  • @juanpa.arrivillaga Makes sense. Thank you for your added clarification. Reading the medium article that AerysS referred me to was very helpful. I think your explanation saves needing to read it. – financial_physician Aug 05 '20 at 06:09