1
class list_class:
        def __init__(self, list_=[]):
                self.list_ = list_

        def add_item(self, item):
                self.list_ += [item]
>>> x = list_class()
>>> y = list_class()
>>> x.list_
[]
>>> y.list_
[]
>>> x.add_item(1)
>>> x.list_
[1]
>>> y.list_
[1]

Why does calling add_item() on x change the list_ variable on both instances?

2 Answers2

1

Using list_=[] in a method argument is a bad Python idiom, for the reason you've discovered. Use the following instead:

class ListClass:
    def __init__(self, list_=None):
        if list_ is None:
            list_ = []
        self.list_ = list_

    def add_item(self, item):
        self.list_.append(item)

list_ is set to [] when __init__ is initially parsed. A list, however, is mutable, and not copied upon assignment. So when the __init__ method is run, each time, it uses the same initial list. With the above replacement, list_ is assigned to a new list every time __init__ is run, and the problem is circumvented. The same holds for a dict as a default for function argument value. It doesn't matter if the list or dict are empty or contain values (__init__(self, list_=[5]) would have had the same problem: don't use a mutable variable in a function argument as a default value.

See the warning in the tutorial section of default function arguments.

Other more Pythonic idioms are using CamelCase for class names, and using somelist.append(item) instead of somelist += [item].

9769953
  • 10,344
  • 3
  • 26
  • 37
0

I've seen this a few times but can't remember the ultimate reason. The solution is to not (or ever) declare empty iterables in your functions/classes.

class List_Class:
    def __init__(self, list_):
        self.list_ = list_

    def add_item(self, item):
        self.list_ += [item]


x = List_Class(list())
y = List_Class(list())
print(x.list_)
print(y.list_)
x.add_item(1)
print(x.list_)
print(y.list_)
zerecees
  • 697
  • 4
  • 13
  • "The solution is to not (or ever) declare empty iterables in your functions/classes": you can set a function argument to a default value of `''` just fine, and a string is iterable. And using `list_=[5]` (non-empty iterable) will have the same problem as that of an empty list. – 9769953 Apr 09 '21 at 04:04