Атрибуты Python и объект / класс - что происходит? - PullRequest
1 голос
/ 15 июня 2010

Может кто-нибудь объяснить, почему 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 ]

7 голосов
/ 15 июня 2010

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

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

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

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

0 голосов
/ 15 июня 2010

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

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 []
0 голосов
/ 15 июня 2010

Как уже говорили другие, написанный код создает переменную класса, а не переменную экземпляра. Вам нужно назначить в __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 (переменная класса доступна через класс, а не через экземпляр) после того, как каждое из ваших утверждений может помочь прояснить, что происходит.

0 голосов
/ 15 июня 2010
>>> 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 по-прежнему является переменной общего класса.

0 голосов
/ 15 июня 2010

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

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

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

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

Foo.bar = 1
0 голосов
/ 15 июня 2010

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

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