Различия между статическими и переменными экземпляра в python. Они даже существуют?
Определение случайного класса:
class ABC:
x = 6
Установка некоторых значений, сначала для экземпляра abc, позже для статической переменной:
abc = ABC()
abc.x = 2
ABC.x = 5
а затем распечатать результаты:
print abc.x
print ABC.x
который печатает
2
5
Теперь я действительно не понимаю, что происходит, потому что, если я заменю в определении класса x = 6 для "pass", он просто выдает то же самое. Мой вопрос в том, какова цель определения переменной в определении класса в python, если мне кажется, что я могу установить в любой момент любую переменную без этого?
Кроме того, знает ли python разницу между экземпляром и статическими переменными? Из того, что я видел, я бы так сказал.
Ответы
Ответ 1
class SomeClass:
x=6 # class variable
def __init__(self):
self.y = 666 # instance variable
В объявлении переменной с областью видимости есть добродетель: она используется по умолчанию для одного. Подумайте о переменной класса, как вы думаете о "статических" переменных на некоторых других языках.
Ответ 2
Предупреждение: следующее упрощение; Я игнорирую __new__()
и кучу других специальных методов класса, а также ручная работа с деталями. Но это объяснение даст вам довольно далеко на Python.
Когда вы создаете экземпляр класса в Python, например, вызываете ABC() в вашем примере:
abc = ABC()
Python создает новый пустой объект и устанавливает его класс на ABC. Затем он вызывает __init__()
, если он есть. Наконец, он возвращает объект.
Когда вы запрашиваете атрибут объекта, сначала он выглядит в экземпляре. Если он не находит его, он выглядит в классе экземпляра. Тогда в базовом классе (es) и так далее. Если он никогда не найдет никого с указанным атрибутом, он выдает исключение.
Когда вы назначаете атрибут объекта, он создает этот атрибут, если объект еще не имеет этого. Затем он устанавливает атрибут этого значения. Если у объекта уже был атрибут с этим именем, он отбрасывает ссылку на старое значение и берет ссылку на новый.
Эти правила делают поведение, которое вы наблюдаете, легко предсказать. После этой строки:
abc = ABC()
только объект ABC (класс) имеет атрибут с именем x. У экземпляра abc еще нет его x, поэтому, если вы попросите его, вы получите значение ABC.x. > . Но затем вы переназначаете атрибут x как для класса, так и для объекта. И когда вы впоследствии изучите эти атрибуты, вы заметите, что значения, которые вы там положили, все еще существуют.
Теперь вы сможете предсказать, что делает этот код:
class ABC:
x = 6
a = ABC()
ABC.xyz = 5
print(ABC.xyz, a.xyz)
Да: он печатает две пятерки. Возможно, вы ожидали, что он вызовет исключение AttributeError. Но Python находит атрибут в классе, даже если он был добавлен после создания экземпляра.
Такое поведение действительно может вызвать у вас неприятности. Одна классическая ошибка начинающего в Python:
class ABC:
x = []
a = ABC()
a.x.append(1)
b = ABC()
print(b.x)
Это напечатает [1]. Все экземпляры ABC() используют один и тот же список. Вероятно, вы хотели:
class ABC:
def __init__(self):
self.x = []
a = ABC()
a.x.append(1)
b = ABC()
print(b.x)
Это будет печатать пустой список, как вы ожидаете.
Чтобы ответить на ваши точные вопросы:
Мой вопрос в том, какова цель определения переменной в определении класса в python, если мне кажется, что я могу в любой момент задать любую переменную без этого?
Я предполагаю, что это означает "почему я должен назначать члены внутри класса, а не внутри метода __init__
?"
Как практический вопрос, это означает, что экземпляры не имеют своей собственной копии атрибута (или, по крайней мере, еще нет). Это означает, что экземпляры меньше; это также означает, что доступ к атрибуту происходит медленнее. Это также означает, что все экземпляры имеют одинаковое значение для этого атрибута, который в случае измененных объектов может быть или не быть тем, что вы хотите. Наконец, присвоения здесь означают, что значение является атрибутом класса, и что самый простой способ установить атрибуты в классе.
Как чисто стилистический вопрос, это более короткий код, так как у вас нет всех этих экземпляров Я.. Кроме того, это не имеет большого значения. Однако присвоение атрибутов в методе __init__
гарантирует, что они являются однозначными переменными экземпляра.
Я не очень уверен в себе. Единственное, что я обязательно сделаю, это назначить все изменяемые объекты, которые я не хочу использовать в методе __init__
.
Кроме того, знает ли python разницу между экземпляром и статическими переменными? Из того, что я видел, я бы так сказал.
В классах Python нет статических переменных класса, таких как С++. Существуют только атрибуты: атрибуты объекта класса и атрибуты объекта экземпляра. И если вы запрашиваете атрибут, а экземпляр не имеет его, вы получите атрибут из класса.
Ближайшим приближением статической переменной класса в Python будет скрытый атрибут модуля, например:
_x = 3
class ABC:
def method(self):
global _x
# ...
Это не часть класса как такового. Но это обычная идиома Python.
Ответ 3
Переменная класса (называемая "статическая" на других языках) принадлежит классу и используется всеми экземплярами класса.
Переменная экземпляра является частью каждого отдельного экземпляра класса.
Однако.
Вы можете добавить новую переменную экземпляра в любое время.
Таким образом, получение abc.x
требует первой проверки переменной экземпляра. Если нет переменной экземпляра, она будет пытаться использовать переменную класса.
И установка abc.x
создаст (или заменит) переменную экземпляра.
Ответ 4
Каждый объект имеет __dict__
. Класс ABC и его экземпляр abc - оба объекта, поэтому каждый имеет свой собственный __dict__
:
In [3]: class ABC:
...: x=6
Примечание ABC.__dict__
имеет ключ "x":
In [4]: ABC.__dict__
Out[4]: {'__doc__': None, '__module__': '__main__', 'x': 6}
In [5]: abc=ABC()
In [6]: abc.__dict__
Out[6]: {}
Обратите внимание, что если "x" не находится в ABC.__dict__
, выполняется поиск __dict__
для суперкласса abs (es). Итак, abc.x
"наследуется" от ABC
:
In [14]: abc.x
Out[14]: 6
Но если мы установим abc.x, мы изменим ABC.__dict__
, а не ABC.__dict__
:
In [7]: abc.x = 2
In [8]: abc.__dict__
Out[8]: {'x': 2}
In [9]: ABC.__dict__
Out[9]: {'__doc__': None, '__module__': '__main__', 'x': 6}
Конечно, мы можем изменить ABC.__dict__
, если хотим:
In [10]: ABC.x = 5
In [11]: ABC.__dict__
Out[11]: {'__doc__': None, '__module__': '__main__', 'x': 5}
Ответ 5
Python делает различие между ними. Цель может быть несколько, но один пример:
class token(object):
id = 0
def __init__(self, value):
self.value = value
self.id = token.id
token.id += 1
Здесь переменная класса token.id
автоматически увеличивается в каждом новом экземпляре, и этот экземпляр может принимать уникальный идентификатор одновременно, который будет помещен в self.id
. Оба они хранятся в разных местах - в объекте класса или в объекте экземпляра вы действительно можете сравнить это со статическими и переменными экземпляра на некоторых языках OO, таких как С++ или С#.
В этом примере, если вы выполните:
print token.id
вы увидите следующий идентификатор, который нужно назначить, тогда как:
x = token(10)
print x.id
предоставит идентификатор этого экземпляра.
Каждый может также поместить другие атрибуты в экземпляр или в класс, это правильно, но это не будет интересно, поскольку код класса не предназначен для их использования. Интерес с примером, приведенным выше, заключается в том, что код класса использует их.
Ответ 6
Преимущество "статического" или в Python "атрибут класса" заключается в том, что каждый экземпляр класса будет иметь доступ к одному и тому же атрибуту класса. Это неверно для атрибутов экземпляра, как вы можете знать.
Возьмем, например:
class A(object):
b = 1
A.b # => 1
inst = A()
inst2 = A()
inst.b # => 1
inst2.b # => 1
A.b = 5
inst.b # => 5
inst2.b # => 5
Как вы можете видеть, экземпляр класса имеет доступ к атрибуту класса, который можно установить, указав имя класса, а затем атрибут класса.
Сложная часть - это когда у вас есть атрибут класса и атрибут экземпляра, который называется одним и тем же. Это требует понимания того, что происходит под капотом.
inst.__dict__ # => {}
A.__dict__ # => {..., 'b': 5}
Обратите внимание на то, что экземпляр не имеет b
в качестве атрибута? Выше, когда мы вызывали inst.b
, Python фактически проверяет inst.__dict__
для атрибута, если он не может быть найден, тогда он ищет A.__dict__
(атрибуты класса). Конечно, когда Python просматривает b
в атрибутах класса, он найден и возвращается.
Вы можете получить какой-то запутанный вывод, если затем установить атрибут экземпляра с тем же именем.
Например:
inst.b = 10
inst.__dict__ #=> {'b': 10}
A.b # => 5
inst.b # => 10
Вы можете видеть, что экземпляр класса теперь имеет атрибут экземпляра b
, и поэтому Python возвращает это значение.