переменные экземпляра класса Python и переменные класса - PullRequest
28 голосов
/ 02 января 2012

У меня проблемы с пониманием того, как переменные класса / экземпляра работают в Python.Я не понимаю, почему, когда я пытаюсь этот код, переменная списка кажется переменной класса

class testClass():
    list = []
    def __init__(self):
        self.list.append('thing')

p = testClass()
print p.list

f = testClass()
print f.list

Вывод:

['thing']
['thing', 'thing']

и когда я делаю это, она кажетсяпеременная экземпляра

class testClass():
    def __init__(self):
        self.list = []
        self.list.append('thing')

p = testClass()
print p.list

f = testClass()
print f.list

Вывод:

['thing']
['thing']

Ответы [ 4 ]

46 голосов
/ 02 января 2012

Это из-за способа, которым Python разрешает имена с ..Когда вы пишете self.list, среда выполнения Python сначала пытается разрешить имя list, ища его в объекте экземпляра, а если оно там не найдено, то в экземпляре класса.

Давайте рассмотримшаг за шагом

self.list.append(1)
  1. Есть ли list имя в объекте self?
    • Да: используйте это!Готово.
    • Нет: перейдите к 2.
  2. Есть ли имя list в экземпляре класса объекта self?
    • Да: используйте это!Готово
    • Нет: ошибка!

Но когда вы связываете имя, все по-другому:

self.list = []
  1. Есть лиlist имя в объекте self?
    • Да: перезаписать его!
    • Нет: связать его!

Итак, это всегда переменная экземпляра.

Ваш первый пример создает list в экземпляре класса, так как это активная область на данный момент (нигде self).Но ваш второй пример создает list явно в области действия self.

Более интересным будет пример:

class testClass():
    list = ['foo']
    def __init__(self):
        self.list = []
        self.list.append('thing')

x = testClass()
print x.list
print testClass.list
del x.list
print x.list

, который напечатает:

['thing']
['foo']
['foo']

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

5 голосов
/ 02 января 2012

В Python есть интересные правила поиска имен. Если вы действительно хотите сойти с ума, попробуйте этот код:

class testClass():
    l = []
    def __init__(self):
        self.l = ['fred']

Это даст каждому экземпляру переменную с именем l, которая маскирует переменную класса l. Вы все еще сможете получить переменную класса, если вы сделаете self.__class__.l.

Я думаю об этом так: всякий раз, когда вы делаете instance.variable (даже для имен методов, это просто переменные, значения которых оказываются функциями), он ищет его в словаре экземпляра. И если он не может найти его там, он пытается найти его в словаре класса экземпляра. Это только если переменная читается. Если ему присвоено, он всегда создает новую запись в словаре экземпляра.

3 голосов
/ 02 января 2012

В вашем первом примере list является атрибутом класса, общим для всех его экземпляров.Это означает, что вы даже можете получить к нему доступ, не имея объекта типа testClass:

>>> class testClass():
...     list = []
...     def __init__(self):
...             self.list.append("thing")
... 
>>> testClass.list
[]
>>> testClass.list.append(1)
>>> testClass.list
[1]

Но все объекты имеют атрибут list с классом и друг с другом:

>>> testObject = testClass()
>>> testObject.list
[1, 'thing']
>>> testClass.list
[1, 'thing']
>>> 
>>> testObject2 = testClass()
>>> testClass.list
[1, 'thing', 'thing']
>>> testObject2.list
[1, 'thing', 'thing']
0 голосов
/ 02 января 2012

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

В первом случае ваш список является атрибутом класса и используется всеми его экземплярами.У вас есть две вещи, потому что вы добавили одну при создании p и другую при создании f (первая уже была добавлена ​​при первом вызове).

...