Когда я создаю экземпляр подкласса python, он перезаписывает атрибут базового класса

Код выглядит так:

class A(object): x = 0 y = 0 z = [] def __init__(self): super(A, self).__init__() if not self.y: self.y = self.x if not self.z: self.z.append(self.x) class B(A): x = 1 class C(A): x = 2 print C().y, C().z print B().y, B().z 

Выход

 2 [2] 1 [2] 

Почему z перезаписывается, но не y ? Это потому, что это не неизменный тип? Я посмотрел документацию на python и не нашел объяснений.

Да, это потому, что он неизменен, а другой нет. Вернее, это то, что вы мутируете одно, а не другое. (Важно то, что объект «изменен», имеет значение, действительно ли вы его мутируете.) Это также потому, что вы используете переменные класса вместо переменных экземпляра (см. Этот вопрос ).

В определении класса вы создаете три переменные класса, разделяемые между всеми экземплярами класса. После создания экземпляра вашего класса, если вы делаете self.x в этом экземпляре, он не найдет x в качестве атрибута экземпляра, но будет искать его в классе. Точно так же self.z будет искать класс и найти тот, что есть на классе. Обратите внимание, что поскольку вы сделали z переменной класса, существует только один список, который будет использоваться для всех экземпляров класса (включая все экземпляры всех подклассов, если они не переопределяют z ).

Когда вы делаете self.y = self.x , вы создаете новый атрибут, атрибут экземпляра , только для экземпляра.

Однако, когда вы делаете self.z.append(...) , вы не создаете новую переменную экземпляра. Скорее, self.z просматривает список, хранящийся в классе, а затем append мутации, которые перечислены. Нет «переписывания». Существует только один список, и когда вы добавляете приложение, вы меняете его содержимое. ( if not self.z только один элемент, потому что у вас есть, if not self.z , поэтому после добавления одного из них это неверно, а последующие вызовы не добавляют ничего больше.)

Результатом является то, что чтение значения атрибута не совпадает с назначением ему. Когда вы читаете значение self.x , вы можете получить значение, которое хранится в классе и разделяемое между всеми экземплярами. Однако, если вы присваиваете значение self.x , вы всегда назначаете атрибут экземпляра; если уже есть атрибут класса с тем же именем, ваш атрибут экземпляра скроет это.

Проблема в том, что x и y неизменяемы, а z изменчиво, и вы мутируете его.

self.z.append() не заменяет z , он просто добавляет элемент в z .

После запуска C() в print C().y, C().z (который создает два разных объекта C ) self.z больше не оценивает False потому что он больше не пуст.

Если вы измените две линии print , вы увидите, что

 1 [1] 2 [1] 

Когда Python оценивает тело class A он создает экземпляр одного объекта list и назначает его z . Так как подклассы не переопределяют его, и поскольку объекты list хранятся по ссылке в Python, все три класса имеют один и тот же список z , поэтому первая, которую вы создаете, получает, чтобы заполнить z а затем остальные просто получают все, что было там поставлено. Хотя вы изменили содержимое списка, вы не изменили, к какому списку z относится.

Это не влияет на y потому что вы назначаете целое число непосредственно во внутренний словарь объекта, заменяя предыдущее значение.

Чтобы исправить это, создайте свой массив внутри конструктора, присвоив:

 class A(object): x = 0 y = 0 z = None def __init__(self): super(A, self).__init__() if not self.y: self.y = self.x if not self.z: # create a new list z = [self.x] class B(A): x = 1 class C(A): x = 2 print C().y, C().z print B().y, B().z