1

Say that I have the following Python code:

import sys

class DogStr:
    tricks = ''

    def add_trick(self, trick):
        self.tricks = trick

class DogList:
    tricks = []

    def add_trick(self, trick):
        self.tricks.append(trick)

# Dealing with DogStr
d = DogStr()
e = DogStr()
d.add_trick('trick d')
e.add_trick('trick e')
print(d.tricks)
print(e.tricks)

# Dealing with DogList
d = DogList()
e = DogList()
d.add_trick('trick d')
e.add_trick('trick e')
print(d.tricks)
print(e.tricks)

Running this code with Python 3.6.5, I get the following output:

trick d
trick e
['trick d', 'trick e']
['trick d', 'trick e']

The difference between DogStr and DogList is that I treat tricks as a string on former and as a list on the latter.

When dealing with DogStr, tricks is behaving as an instance variable. BUT with DogList tricks is behaving as a class variable.

I was expecting to see the same behaviour on both calls, i.e.: if the two last lines of the output are identical, so should be the first two.

So I wonder. What is the explanation for that?

Diogo Melo
  • 1,735
  • 3
  • 20
  • 29
  • 1
    They're not behaving differently. If you do `self.tricks = trick` with your list, you will see exactly the same behavior as with strings. The difference is that you're doing something different—you're mutating the list value, which you aren't doing with the string value (and, in fact, can't do, because strings are immutable). – abarnert Jun 01 '18 at 21:51
  • Anyway, this is definitely a duplicate of a common question, but I'm looking for the best answer. Meanwhile, [this one](https://stackoverflow.com/questions/2612802/how-to-clone-or-copy-a-list) and [this one](https://stackoverflow.com/questions/37333000/what-is-the-difference-between-assign-list-with-x-list-and-x-list) have some useful info, even if they aren't duplicates. – abarnert Jun 01 '18 at 21:55
  • I'm inexperienced with Python. So I didn't know that this was a common question. I couldn't find an equivalent question but, thank you for pointing related questions. – Diogo Melo Jun 01 '18 at 22:11
  • Also see [this one](https://stackoverflow.com/questions/1680528/how-to-avoid-having-class-data-shared-among-instances) and [this one](https://stackoverflow.com/questions/2923579/python-class-attribute) and [this one](https://stackoverflow.com/questions/19721002/is-a-variable-the-name-the-value-or-the-memory-location?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa), but I'm giving up the search for now; hopefully someone else finds the dup. – abarnert Jun 01 '18 at 22:14
  • The rule is that you're supposed to search for dups before posting… but the automated dup finder is useless, and, as you can see, I'm having a hard time finding the dup for this one despite knowing the python tag here pretty well and having found it in the past, so… I'd hardly blame you for not being able to find it. – abarnert Jun 01 '18 at 22:15

1 Answers1

3

The difference is not int the type of the object, but in what your code does to it.

There is a big difference between these two:

self.tricks = trick

and:

self.tricks.append(trick)

The first one self.tricks = trick assigns a value to attribute tricks of self.

The second one self.tricks.append(trick) retrieves self.tricks and calls a method on it (which here modifies its values).


The problem, in your case, is that there is no tricks defined on self instance, so self.tricks.append gets the tricks attribute of the class and modifies it, but self.tricks = ... creates a new attribute on self instead.

The fact that one of them is a string and the other is a list is not really relevant. It would be the same if both were lists. Note that they could not both be strings because strings are immutable and thus have no append method

How to fix it?

This is wrong:

def add_trick(self, trick):
    self.tricks = trick

If tricks is a class attribute, add_trick should be a class method:

@classmethod
def add_trick(cls, trick):
    cls.tricks = trick

If there are reasons for add_trick to be an instance method, then simply do this:

def add_trick(self, trick):
    DogStr.tricks = trick
zvone
  • 18,045
  • 3
  • 49
  • 77
  • The point of initialization, in relation to the scope, really got me confused. Thank you for clearing this up. – Diogo Melo Jun 01 '18 at 22:06