Атрибуты Python и объект / класс - что происходит?

Может кто-нибудь объяснить, почему Python делает следующее?

>>> class Foo(object):
...   bar = []
...
>>> a = Foo()
>>> b = Foo()
>>> a.bar.append(1)
>>> b.bar
[1]
>>> a.bar = 1
>>> a.bar
1
>>> b.bar
[1]
>>> a.bar = []
>>> a.bar
[]
>>> b.bar
[1]
>>> del a.bar
>>> a.bar
[1]

Это довольно запутанно!

6 ответов

Решение

Как уже говорили другие, написанный код создает переменную класса, а не переменную экземпляра. Вы должны назначить в __init__ создать переменную экземпляра.

Надеемся, что эта аннотированная копия вашего кода поможет объяснить, что происходит на каждом этапе:

>>> class Foo(object):
...   bar = []          # defines a class variable on Foo (shared by all instances)
...
>>> a = Foo()
>>> b = Foo()
>>> a.bar.append(1)     # appends the value 1 to the previously empty list Foo.bar
>>> b.bar               # returns the value of the class variable Foo.bar
[1]
>>> a.bar = 1           # binds 1 to the instance variable a.bar, masking the access
>>> a.bar               # you previously had to the class variable through a.bar
1
>>> b.bar               # b doesn't have an instance variable 'bar' so this still
[1]                     # returns the class variable
>>> a.bar = []          # bind a's instance variable to to an empty list
>>> a.bar
[]
>>> b.bar               # b doesn't have an instance variable 'bar' so this still
[1]                     # returns the class variable
>>> del a.bar           # unbinds a's instance variable unmasking the class variable
>>> a.bar               # so a.bar now returns the list with 1 in it.
[1]

Кроме того, распечатывая значение Foo.bar (переменная класса доступна через класс, а не через экземпляр) после каждого из ваших утверждений может помочь прояснить, что происходит.

Это потому, что, как вы это написали, bar является переменной класса, а не переменной экземпляра.

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

class Foo(object):
  def __init__(self):
    self.bar = []

Обратите внимание, что теперь он принадлежит одному экземпляру Foo (self) а не Foo класс, и вы увидите результаты, которые вы ожидаете, когда вы назначаете на него.

Когда вы объявляете элемент в классе таким образом, он используется всеми экземплярами класса. Чтобы сделать правильный член класса, который принадлежит каждому экземпляру, отдельно, создайте его в __init__, как показано ниже:

class Foo(object):
    def __init__(self):
        self.bar = []

На связанной ноте вы должны знать об этой ловушке, которая может появиться в ближайшее время:

class A:
   def __init__(self, mylist = []):
      self.mylist = mylist


a = A()
a2 = A()

a.mylist.append(3)
print b.mylist #prints [3] ???

Это сбивает с толку многих людей и связано с тем, как интерпретируется код. Python сначала интерпретирует заголовки функций, поэтому он оценивает __init__(self, mylist = []) и сохраняет ссылку на этот список в качестве параметра по умолчанию. Это означает, что все экземпляры A будут (если не предоставлен их собственный список) ссылаться на исходный список. Правильный код для такой вещи будет

class A:
   def __init__(self, mylist=None):
      if mylist:
         self.mylist = mylist
      else:
         self.mylist = []

или если вы хотите более короткое выражение, вы можете использовать троичный синтаксис:

self.mylist = mylist if mylist else []
>>> class Foo(object):
...   bar = []
...

bar является переменной общего класса, а не переменной экземпляра. Я полагаю, что это связано с большей частью вашей путаницы. Чтобы сделать его экземпляром var, определите его в классе __init__ за другие ответы.

>>> a = Foo()
>>> b = Foo()
>>> a.bar.append(1)
>>> b.bar
[1]

Это доказательство этого.

>>> a.bar = 1
>>> a.bar
1
>>> b.bar
[1]

Теперь вы переопределены a.bar в качестве переменной экземпляра. Вот что происходит, когда вы определяете переменные внешне по умолчанию.

>>> a.bar = []
>>> a.bar
[]
>>> b.bar
[1]
>>> del a.bar
>>> a.bar
[1]

Опять то же самое. b.bar по-прежнему является переменной общего класса.

В начале, bar переменная класса, и она разделяется между a а также b, и то и другое a.bar а также b.bar ссылаются на тот же объект.

Когда вы назначаете новое значение a.bar, это не перезаписывает переменную класса, это добавляет новую переменную экземпляра к a объект, скрывающий переменную класса при доступе a.bar, Если вы удалите a.bar (переменная экземпляра), затем a.bar разрешается снова в переменную класса.

b.bar с другой стороны, всегда относится к переменной класса, на нее не влияют дополнительные bar на a объект или любые значения, присвоенные этому.

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

Foo.bar = 1
Другие вопросы по тегам