Атрибуты 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