Подтвердить что ты не робот

В чем разница между переменными класса и экземпляра?

В чем разница между переменными класса и экземпляра в Python?

class Complex:
    a = 1

и

class Complex:
    def __init__(self):
        self.a = 1

Использование вызова: x = Complex().a в обоих случаях присваивает x 1.

Будет оценен более подробный ответ о __init__() и self.

4b9b3361

Ответ 1

Когда вы пишете блок класса, вы создаете атрибуты класса (или переменные класса). Все имена, которые вы назначили в блоке классов, включая методы, которые вы определяете с помощью def, становятся атрибутами класса.

После создания экземпляра класса все, что ссылается на экземпляр, может создавать на нем атрибуты экземпляра. Внутри методов "текущий" экземпляр почти всегда связан с именем self, поэтому вы думаете об этом как о "самопеременных переменных". Обычно в объектно-ориентированном дизайне код, прикрепленный к классу, должен иметь контроль над атрибутами экземпляров этого класса, поэтому почти все атрибуты атрибутов экземпляра выполняются внутри методов, используя ссылку на экземпляр, полученный в self параметр метода.

Атрибуты класса часто сравниваются со статическими переменными (или методами), которые можно найти на таких языках, как Java, С# или С++. Однако, если вы хотите стремиться к более глубокому пониманию, я бы избегал думать о атрибутах класса как о "тех же", что и статические переменные. Хотя они часто используются для одних и тех же целей, базовая концепция совершенно иная. Подробнее об этом в разделе "Расширенный" ниже строки.

Пример!

class SomeClass:
    def __init__(self):
        self.foo = 'I am an instance attribute called foo'
        self.foo_list = []

    bar = 'I am a class attribute called bar'
    bar_list = []

После выполнения этого блока существует класс SomeClass с тремя атрибутами класса: __init__, bar и bar_list.

Затем мы создадим экземпляр:

instance = SomeClass()

Когда это произойдет, выполняется SomeClass __init__ метод, получив новый экземпляр в параметре self. Этот метод создает два атрибута экземпляра: foo и foo_list. Затем этот экземпляр присваивается переменной instance, поэтому он связан с вещью с этими двумя атрибутами экземпляра: foo и foo_list.

Но:

print instance.bar

дает:

I am a class attribute called bar

Как это произошло? Когда мы пытаемся получить атрибут через синтаксис точек, а атрибут не существует, Python выполняет множество шагов, чтобы попытаться выполнить ваш запрос в любом случае. Следующее, что он попробует, это посмотреть на атрибуты класса класса вашего экземпляра. В этом случае он обнаружил атрибут bar в SomeClass, поэтому он вернул это.

Это также способ, которым работают вызовы методов. Когда вы вызываете mylist.append(5), например, mylist не имеет атрибута с именем append. Но класс mylist имеет и связан с объектом метода. Этот объект метода возвращается бит mylist.append, а затем бит (5) вызывает метод с аргументом 5.

Способ, которым это полезно, состоит в том, что все экземпляры SomeClass будут иметь доступ к одному и тому же атрибуту bar. Мы могли бы создать миллион экземпляров, но нам нужно сохранить только одну строку в памяти, потому что они все могут ее найти.

Но вы должны быть немного осторожны. Посмотрите на следующие операции:

sc1 = SomeClass()
sc1.foo_list.append(1)
sc1.bar_list.append(2)

sc2 = SomeClass()
sc2.foo_list.append(10)
sc2.bar_list.append(20)

print sc1.foo_list
print sc1.bar_list

print sc2.foo_list
print sc2.bar_list

Как вы думаете, что это печатает?

[1]
[2, 20]
[10]
[2, 20]

Это потому, что каждый экземпляр имеет свою собственную копию foo_list, поэтому они были добавлены отдельно. Но все экземпляры имеют доступ к тому же bar_list. Поэтому, когда мы сделали sc1.bar_list.append(2), это затронуло sc2, хотя sc2 еще не существовало! А также sc2.bar_list.append(20) повлияло на bar_list, извлеченный через sc1. Это часто не то, что вы хотите.


Далее следует расширенное исследование.:)

Чтобы действительно нагромождать Python, исходя из традиционных статически типизированных OO-языков, таких как Java и С#, вам нужно немного научиться переосмысливать классы.

В Java класс не является само по себе. Когда вы пишете класс, вы больше объявляете кучу вещей, которые имеют все экземпляры этого класса. Во время выполнения есть только экземпляры (и статические методы/переменные, но на самом деле это просто глобальные переменные и функции в пространстве имен, связанных с классом, не имеет ничего общего с OO). Классы - это то, как вы записываете в своем исходном коде, какими будут экземпляры во время выполнения; они только "существуют" в исходном коде, а не в запущенной программе.

В Python класс ничего особенного. Это объект, как и все остальное. Таким образом, "атрибуты класса" на самом деле являются точно такими же, как "атрибуты экземпляра"; на самом деле там просто "атрибуты". Единственная причина для разграничения состоит в том, что мы склонны использовать объекты, которые являются классами, отличными от объектов, которые не являются классами. Основополагающий механизм все тот же. Вот почему я говорю, что было бы ошибкой думать о атрибутах класса как статических переменных с других языков.

Но то, что действительно делает классы Python отличными от классов стиля Java, заключается в том, что, как и любой другой объект , каждый класс является экземпляром некоторого класса!

В Python большинство классов являются экземплярами встроенного класса с именем type. Именно этот класс контролирует общее поведение классов и делает все OO файлы такими, как он. Способ OO по умолчанию для экземпляров классов, которые имеют свои собственные атрибуты, и имеет общие методы/атрибуты, определенные их классом, является всего лишь протоколом на Python. Вы можете изменить большинство его аспектов, если хотите. Если вы когда-либо слышали о применении метакласса, все это определение класса, который является экземпляром другого класса, чем type.

Единственная действительно "особенная" вещь в классах (помимо всего встроенного механизма, чтобы заставить их работать так, как они делают по умолчанию), является синтаксисом блока классов, чтобы упростить создание экземпляров type, Это:

class Foo(BaseFoo):
    def __init__(self, foo):
        self.foo = foo

    z = 28

примерно эквивалентно следующему:

def __init__(self, foo):
    self.foo = foo

classdict = {'__init__': __init__, 'z': 28 }

Foo = type('Foo', (BaseFoo,) classdict)

И он будет обеспечивать, чтобы все содержимое classdict стало атрибутом создаваемого объекта.

Итак, становится почти тривиально видеть, что вы можете получить доступ к атрибуту класса Class.attribute так же легко, как i = Class(); i.attribute. Оба i и Class являются объектами, а объекты имеют атрибуты. Это также позволяет легко понять, как вы можете изменить класс после его создания; просто назначьте его атрибуты так же, как и с любым другим объектом!

Фактически, экземпляры не имеют особых особых отношений с классом, используемым для их создания. Способ, которым Python знает, какой класс ищет атрибуты, которые не найдены в экземпляре, - это скрытый атрибут __class__. Что вы можете прочитать, чтобы узнать, какой класс это экземпляр, так же как и с любым другим атрибутом: c = some_instance.__class__. Теперь у вас есть переменная c, связанная с классом, хотя она, вероятно, не имеет того же имени, что и класс. Вы можете использовать это для доступа к атрибутам класса или даже вызвать его для создания большего количества экземпляров (даже если вы не знаете, какой класс он есть!).

И вы даже можете назначить i.__class__, чтобы изменить класс, которым он является экземпляр! Если вы это сделаете, ничего особенного не произойдет немедленно. Это не разрушительно. Все это означает, что при поиске атрибутов, которые не существуют в экземпляре, Python пойдет посмотреть новое содержимое __class__. Поскольку это включает большинство методов, и методы обычно ожидают, что экземпляр, в котором они работают, находятся в определенных состояниях, обычно это приводит к ошибкам, если вы делаете это наугад, и это очень запутанно, но это можно сделать. Если вы очень осторожны, то вещь, которую вы храните в __class__, даже не должна быть объектом класса; все Python собирается с ним искать атрибуты при определенных обстоятельствах, поэтому все, что вам нужно, это объект, который имеет правильные типы атрибутов (некоторые оговорки в сторону, где Python действительно придирчивы к вещам, являющимся классами или экземплярами определенного класса).

Скорее всего, сейчас. Надеюсь (если вы даже зачитали это далеко), я не слишком вас смутил. Python опрятен, когда вы узнаете, как это работает.:)

Ответ 2

То, что вы называете переменной "экземпляр", на самом деле не является переменной экземпляра; это переменная класса . См. Ссылку о классах.

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

>>> class Complex:
>>>    a = []
>>>
>>> b = Complex()
>>> c = Complex()
>>> 
>>> # What do they look like?
>>> b.a
[]
>>> c.a
[]
>>> 
>>> # Change b...
>>> b.a.append('Hello')
>>> b.a
['Hello']
>>> # What does c look like?
>>> c.a
['Hello']

Если вы использовали self, то это была бы истинная переменная экземпляра, и, таким образом, каждый экземпляр имел бы свой уникальный a. Функция __init__ объекта вызывается при создании нового экземпляра, а self является ссылкой на этот экземпляр.