Что я хочу, это поведение:
class a:
list = []
x = a()
y = a()
x.list.append(1)
y.list.append(2)
x.list.append(3)
y.list.append(4)
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
. Как получить отдельные экземпляры для достижения желаемого поведения?
list
в качестве имени атрибута.list
является встроенной функцией для создания нового списка Вы должны написать имя классов с заглавной буквы.Ответы:
Вы хотите это:
Объявление переменных внутри объявления класса делает их членами класса, а не членами экземпляра. Объявление их внутри
__init__
метода гарантирует, что новый экземпляр членов будет создан рядом с каждым новым экземпляром объекта, что является поведением, которое вы ищете.источник
x.list = []
, вы можете изменить это и не влиять на других. Проблема, с которой вы сталкиваетесь, состоит в том, что они представляют собой одинx.list
иy.list
тот же список, поэтому, когда вы вызываете append для одного, это влияет на другой.__init__
.Принятый ответ работает, но немного больше объяснений не повредит.
Атрибуты класса не становятся атрибутами экземпляра при создании экземпляра. Они становятся атрибутами экземпляра, когда им присваивается значение.
В исходном коде значение не присваивается
list
атрибуту после создания экземпляра; поэтому он остается атрибутом класса. Определение списка внутри__init__
работает, потому что__init__
вызывается после создания экземпляра. Кроме того, этот код также будет производить желаемый результат:Однако запутанный сценарий в вопросе никогда не случится с неизменными объектами, такими как числа и строки, потому что их значение нельзя изменить без присваивания. Например, код, похожий на оригинал со строковым атрибутом, работает без проблем:
Итак, подведем итог: атрибуты класса становятся атрибутами экземпляра тогда и только тогда, когда им присваивается значение после создания экземпляра, находящегося в
__init__
методе или нет . Это хорошо, потому что таким образом вы можете иметь статические атрибуты, если вы никогда не назначаете значение атрибуту после создания экземпляра.источник
Вы объявили «список» как «свойство уровня класса», а не как «свойство уровня экземпляра». Для того чтобы свойства находились в области видимости на уровне экземпляра, вам необходимо инициализировать их, ссылаясь на параметр «self» в
__init__
методе (или в других местах, в зависимости от ситуации).Вам не обязательно инициализировать свойства экземпляра в
__init__
методе, но это облегчает понимание.источник
Хотя принятый ответ находится на месте, я хотел бы добавить немного описания.
Давайте сделаем небольшое упражнение
Прежде всего определите класс следующим образом:
Так что у нас здесь?
temp
который является строкой__init__
Метод , который устанавливаетself.x
self.temp
Довольно прямо вперед, да? Теперь давайте начнем играть с этим классом. Давайте сначала инициализируем этот класс:
Теперь сделайте следующее:
Ну,
a.temp
работал, как ожидалось, но как, черт возьми,A.temp
работал? Ну, это сработало, потому что temp является атрибутом класса. Все в питоне является объектом. Здесь А также является объектом класса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)
, они будут другими.источник
Да, вы должны объявить в «конструкторе», если хотите, чтобы список стал свойством объекта, а не свойством класса.
источник
Таким образом, почти каждый ответ здесь, кажется, пропускает определенный момент. Переменные класса никогда не становятся переменными экземпляра, как показано в коде ниже. Используя метакласс для перехвата присваивания переменных на уровне класса, мы можем видеть, что когда a.myattr переназначается, магический метод назначения поля в классе не вызывается. Это потому, что присваивание создает новую переменную экземпляра . Это поведение не имеет абсолютно никакого отношения к переменной класса, что продемонстрировано вторым классом, который не имеет переменных класса и все же позволяет присваивать поля.
IN SHORT Переменные класса не имеют ничего общего с переменными экземпляра.
Более ясно, что они просто попадают в область поиска экземпляров. Переменные класса на самом деле являются переменными экземпляра самого объекта класса. Вы также можете иметь переменные метакласса, если хотите, потому что сами метаклассы тоже являются объектами. Все является объектом, независимо от того, используется ли он для создания других объектов или нет, поэтому не ограничивайтесь семантикой использования слова в классе других языков. В Python класс - это просто объект, который используется для определения того, как создавать другие объекты и как они будут себя вести. Метаклассы - это классы, которые создают классы, просто чтобы проиллюстрировать этот момент.
источник
Чтобы защитить вашу переменную, совместно используемую другим экземпляром, вам нужно создавать новую переменную экземпляра каждый раз, когда вы создаете экземпляр. Когда вы объявляете переменную внутри класса, она является переменной класса и используется всеми экземплярами. Если вы хотите сделать это, например, необходимо использовать инициализацию метод для повторной инициализации переменной, как указано в экземпляре
Из объектов и классов Python от Programiz.com :
Например:
источник