Список, который указан, как атрибут экземпляра класса остается общим для всех остальных экземпляров данного класса

Создаю класс, атрибутом которого является список с уникальными значениями для каждого экземпляра.

class FooClass:
    def __init__(self, list_attr=[]):
        # Список, который должен быть уникальным для каждого нового экземпляра класса FooClass
        self.list_attr = list_attr

    def add_to_list_attr(self, new_item):
        """метод, для добавления объектов в список self.list_attr, 
           который предполагался как уникальный для каждого экземпляра 
           класса FooClass"""
        self.list_attr.append(new_item)

Если я правильно понимаю смысл ключевого слова self в Питоне, то оно должно сделать список self.list_attr уникальным для каждого нового создаваемого объекта FooClass. Поэтому в методе add_to_list_attr(self, new_item) первым аргументом также указано self.

Однако на выходе я получаю это:

>>> foo1 = FooClass()

>>> foo1.list_attr
[]

>>> foo1.add_to_list_attr(1)

>>> foo1.list_attr
[1]

>>> foo2 = FooClass()

>>> foo2.list_attr
[1]

>>> foo2.add_to_list_attr(2)

>>> foo2.list_attr
[1, 2]

>>> foo1.list_attr
[1, 2]

То есть self.list_attr ведет себя, словно это статичный атрибут для класса в целом, а не атрибут экземпляра класса. Объясните, пожалуйста, в чем причина и как добиться желаемого поведения.


Ответы (1 шт):

Автор решения: CrazyElf

Это известная фича, если вкратце, то нужно делать так:

def __init__(self, list_attr=None):
    # Список, который должен быть уникальным для каждого нового экземпляра класса FooClass
    if list_attr is None:
        list_attr = []
    self.list_attr = list_attr

Смысл примерно в том, что если вы задаёте дефолтный параметр функции, то он инициализируется только один раз. При создании новых экземпляров класса новой инициализации уже не происходит. Поэтому в дефолтных параметрах нужно использовать не пустой список, а None, а уже внутри функции проверять на None и создавать новый список. Это просто нужно знать.

И проявляется это не только в методах класса, а в любых функциях:

def myfunc(mylist = []):
    mylist.append(1)
    return mylist

myfunc()
print(myfunc())

Вывод:

[1, 1]
→ Ссылка