Вопрос-Ответ

How to avoid having class data shared among instances?

Как избежать совместного использования данных класса между экземплярами?

Чего я хочу, так это такого поведения:

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. Как мне получить отдельные экземпляры для достижения желаемого поведения?

Переведено автоматически
Ответ 1

Вы хотите этого:

class a:
def __init__(self):
self.list = []

Объявление переменных внутри объявления класса делает их членами "класса", а не членами экземпляра. Их объявление внутри __init__ метода гарантирует, что новый экземпляр элементов создается рядом с каждым новым экземпляром объекта, что соответствует тому поведению, которое вы ищете.

Ответ 2

Принятый ответ работает, но немного дополнительных объяснений не помешает.

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

В исходном коде list атрибуту после создания экземпляра не присваивается значение; поэтому он остается атрибутом класса. Определение списка внутри __init__ работает, потому что __init__ вызывается после создания экземпляра. В качестве альтернативы, этот код также будет выдавать желаемый результат:

>>> class a:
list = []

>>> y = a()
>>> x = a()
>>> x.list = []
>>> y.list = []
>>> x.list.append(1)
>>> y.list.append(2)
>>> x.list.append(3)
>>> y.list.append(4)
>>> print(x.list)
[1, 3]
>>> print(y.list)
[2, 4]

Однако запутанный сценарий, описанный в вопросе, никогда не произойдет с неизменяемыми объектами, такими как числа и строки, потому что их значение нельзя изменить без присвоения. Например, код, аналогичный исходному, с типом атрибута string, работает без каких-либо проблем:

>>> class a:
string = ''


>>> x = a()
>>> y = a()
>>> x.string += 'x'
>>> y.string += 'y'
>>> x.string
'x'
>>> y.string
'y'

Итак, подведем итог: атрибуты класса становятся атрибутами экземпляра тогда и только тогда, когда им присваивается значение после создания экземпляра, будь то в __init__ методе или нет. Это хорошо, потому что таким образом вы можете иметь статические атрибуты, если вы никогда не присваиваете значение атрибуту после создания экземпляра.

Ответ 3

Хотя принятый ответ точен, я хотел бы добавить краткое описание.

Давайте выполним небольшое упражнение

прежде всего определите класс следующим образом:

class A:
temp = 'Skyharbor'

def __init__(self, x):
self.x = x

def change(self, y):
self.temp = y

Итак, что мы здесь имеем?


  • У нас есть очень простой класс, у которого есть атрибут, temp который является строкой

  • __init__ Метод, который устанавливает self.x

  • Метод изменения устанавливает self.temp

Пока довольно прямолинейно, да? Теперь давайте начнем играть с этим классом. Давайте сначала инициализируем этот класс:

a = A('Tesseract')

Теперь выполните следующее:

>>> print(a.temp)
Skyharbor
>>> print(A.temp)
Skyharbor

Ну, a.temp сработало, как ожидалось, но как, черт возьми, A.temp сработало? Ну, это сработало, потому что temp - это атрибут класса. Все в python является объектом. Здесь A также является объектом класса type. Таким образом, атрибут temp является атрибутом, удерживаемым A классом, и если вы измените значение temp через A (а не через экземпляр a), измененное значение будет отражено во всех экземплярах A класса.
Давайте продолжим и сделаем это:

>>> A.temp = 'Monuments'
>>> print(A.temp)
Monuments
>>> print(a.temp)
Monuments

Интересно, не правда ли? И обратите внимание, что id(a.temp) и id(A.temp) все те же.

Любому объекту Python автоматически присваивается __dict__ атрибут, который содержит его список атрибутов. Давайте исследуем, что содержит этот словарь для наших примеров объектов:

>>> print(A.__dict__)
{
'change': <function change at 0x7f5e26fee6e0>,
'__module__': '__main__',
'__init__': <function __init__ at 0x7f5e26fee668>,
'temp': 'Monuments',
'__doc__': None
}
>>> print(a.__dict__)
{x: 'Tesseract'}

Обратите внимание, что 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 методом:

>>> a.change('Intervals')
>>> print(a.temp)
Intervals
>>> print(A.temp)
Monuments

Что ж, теперь, когда мы использовали self, print(a.temp) дает нам значение, отличное от print(A.temp).

Теперь, если мы сравним id(a.temp) и id(A.temp), они будут разными.

Ответ 4

Вы объявили "список" как "свойство уровня класса", а не "свойство уровня экземпляра". Чтобы ограничить область действия свойств на уровне экземпляра, вам необходимо инициализировать их путем обращения к параметру "self" в __init__ методе (или в другом месте, в зависимости от ситуации).

Вам не обязательно строго инициализировать свойства экземпляра в __init__ методе, но это упрощает понимание.

python class