Здравствуйте, пытаюсь разбираться с ООП в 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]
Ответ
Потому что значения по-умолчанию параметров функции или метода создаются не каждый раз при вызове этой функции/метода, а единожды в момент из создания. Смотрите, когда создается класс 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
...
Комментариев нет:
Отправить комментарий