print(x.list) # prints [1, 3] print(y.list) # prints [2, 4]
Конечно, что на самом деле происходит при печати, так это:
print(x.list) # prints [1, 2, 3, 4] print(y.list) # prints [1, 2, 3, 4]
Очевидно, что они совместно используют данные в классе a. Как мне получить отдельные экземпляры для достижения желаемого поведения?
Переведено автоматически
Ответ 1
Вы хотите этого:
classa: def__init__(self): self.list = []
Объявление переменных внутри объявления класса делает их членами "класса", а не членами экземпляра. Их объявление внутри __init__ метода гарантирует, что новый экземпляр элементов создается рядом с каждым новым экземпляром объекта, что соответствует тому поведению, которое вы ищете.
Ответ 2
Принятый ответ работает, но немного дополнительных объяснений не помешает.
Атрибуты класса не становятся атрибутами экземпляра при создании экземпляра. Они становятся атрибутами экземпляра, когда им присваивается значение.
В исходном коде list атрибуту после создания экземпляра не присваивается значение; поэтому он остается атрибутом класса. Определение списка внутри __init__ работает, потому что __init__ вызывается после создания экземпляра. В качестве альтернативы, этот код также будет выдавать желаемый результат:
Однако запутанный сценарий, описанный в вопросе, никогда не произойдет с неизменяемыми объектами, такими как числа и строки, потому что их значение нельзя изменить без присвоения. Например, код, аналогичный исходному, с типом атрибута string, работает без каких-либо проблем:
Итак, подведем итог: атрибуты класса становятся атрибутами экземпляра тогда и только тогда, когда им присваивается значение после создания экземпляра, будь то в __init__ методе или нет. Это хорошо, потому что таким образом вы можете иметь статические атрибуты, если вы никогда не присваиваете значение атрибуту после создания экземпляра.
Ответ 3
Хотя принятый ответ точен, я хотел бы добавить краткое описание.
Давайте выполним небольшое упражнение
прежде всего определите класс следующим образом:
classA: temp = 'Skyharbor'
def__init__(self, x): self.x = x
defchange(self, y): self.temp = y
Итак, что мы здесь имеем?
У нас есть очень простой класс, у которого есть атрибут, temp который является строкой
__init__ Метод, который устанавливает self.x
Метод изменения устанавливает self.temp
Пока довольно прямолинейно, да? Теперь давайте начнем играть с этим классом. Давайте сначала инициализируем этот класс:
Ну, a.temp сработало, как ожидалось, но как, черт возьми, A.temp сработало? Ну, это сработало, потому что temp - это атрибут класса. Все в python является объектом. Здесь A также является объектом класса type. Таким образом, атрибут temp является атрибутом, удерживаемым A классом, и если вы измените значение temp через A (а не через экземпляр a), измененное значение будет отражено во всех экземплярах A класса. Давайте продолжим и сделаем это:
Интересно, не правда ли? И обратите внимание, что id(a.temp) и id(A.temp) все те же.
Любому объекту Python автоматически присваивается __dict__ атрибут, который содержит его список атрибутов. Давайте исследуем, что содержит этот словарь для наших примеров объектов:
Обратите внимание, что temp атрибут указан среди A атрибутов класса, в то время как x указан для экземпляра.
Итак, как получилось, что мы получаем определенное значение a.temp, если его даже нет в списке для экземпляра a. Что ж, в этом и заключается магия __getattribute__() метода. В Python синтаксис с точками автоматически вызывает этот метод, поэтому, когда мы пишем a.temp, Python выполняет a.__getattribute__('temp'). Этот метод выполняет действие поиска атрибута, то есть находит значение атрибута путем поиска в разных местах.
Стандартная реализация __getattribute__() выполняет поиск сначала по внутреннему словарю (dict) объекта, затем по типу самого объекта. В этом случае a.__getattribute__('temp') выполняется сначала a.__dict__['temp'], а затем a.__class__.__dict__['temp']
Хорошо, теперь давайте воспользуемся нашим change методом:
Что ж, теперь, когда мы использовали self, print(a.temp) дает нам значение, отличное от print(A.temp).
Теперь, если мы сравним id(a.temp) и id(A.temp), они будут разными.
Ответ 4
Вы объявили "список" как "свойство уровня класса", а не "свойство уровня экземпляра". Чтобы ограничить область действия свойств на уровне экземпляра, вам необходимо инициализировать их путем обращения к параметру "self" в __init__ методе (или в другом месте, в зависимости от ситуации).
Вам не обязательно строго инициализировать свойства экземпляра в __init__ методе, но это упрощает понимание.