2013-03-18 6 views
3

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

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 и не нашел объяснений.

ответ

3

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

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

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

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

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

+0

спасибо, что имеет смысл –

0

Вопрос заключается в том, что x и y являются неизменяемыми, а z является изменяемым, и вы его мутируете.

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

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

Если поменять местами две print линии, вы сможете найти выход является

1 [1] 
2 [1] 
0

Когда 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 
Смежные вопросы