Страницы

Поиск по вопросам

воскресенье, 15 марта 2020 г.

Почему меняется атрибут в конструкторе экземпляра класса?

#python #python_3x


Здравствуйте, пытаюсь разбираться с ООП в python, и возник такой вопрос:
я создаю 2 экземпляра класса ListAdder. Сразу после создания первого объекта я меняю
ему атрибут x.data и затем создаю второй объект этого же класса, но при этом атрибут
y.data - уже не пустой список, который инициализируется в конструкторе, а такой же,
как и x.data. Почему так происходит? Разве каждый новый объект не должен создаваться
с атрибутом self.data, равным пустому списку?

class ListAdder:
    def __init__(self, start=[]):
        self.data = start

    def add(self, other):
        self.data.extend(other)
        return self.data


if __name__ == '__main__':
    x = ListAdder()
    x.add([1, 2])
    print(x.data)
    y = ListAdder()
    print(y.data)

>>> [1, 2]
>>> [1, 2]

    


Ответы

Ответ 1



Потому что значения по-умолчанию параметров функции или метода создаются не каждый раз при вызове этой функции/метода, а единожды в момент из создания. Смотрите, когда создается класс ListAdder, создаются так же и его методы, и тогда создается объект-пустой список, и указывается, что он является значением по-умолчанию для параметра start метода __init__, причем этот один и тот же объект будет значением по-умолчанию для всех вызовов этой функции. Когда объект неизменяемый, как, например, число или кортеж, это проблемы не вызывает, но когда он изменяемый, то любые изменения, сделанные в этом объекте, в одном из вызовов функции, отражаются на остальных вызовах. Вот более простой и наглядный пример, показывающий суть проблемы: >>> def test(l=[]): ... print('id:', id(l)) ... l.append(1) ... return l ... >>> test() id: 139654229177416 [1] >>> test() id: 139654229177416 [1, 1] >>> test() id: 139654229177416 [1, 1, 1] Функция id возвращает уникальный идентификатор объекта, и тут мы можем видеть, что при каждом вызове test, l ссылается на один и тот же список, что и подтверждается возвращаемым значением функции: при каждом вызове список l увеличивается на один элемент. Чтоб избежать этой проблемы, нужно создавать изменяемый объект при каждом вызове самостоятельно. Классическим способом это сделать является установка значения по-умолчанию в None с последующей проверкой на то, было ли передано какое-то иное значение, или нет, и в последнем случае, создание нужного объекта (пустого списка в вашем случае). class ListAdder: def __init__(self, start=None): if start is None: # Здесь каждый раз, когда start задан по умолчанию, # мы создаем новый экземпляр пустого списка, и # присваиваем его переменной start start = [] # Теперь все последующие изменения списка start не # затронут остальные вызовы метода. self.data = start ...

Комментариев нет:

Отправить комментарий