Различия между статическими и переменными экземпляра в 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 возвращает это значение.