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


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

class Complex:
    a = 1

и

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

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

более глубокий ответ о __init__() и self будут оценены.

2 52

2 ответа:

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

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

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

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 и C#, вы должны научиться немного переосмыслить классы.

в 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 знает, какой класс для поиска атрибутов, которые не найдены в экземпляре, является скрытым . Который вы можете прочитать, чтобы узнать, какой класс это экземпляр, как и с любым другим атрибутом:c = some_instance.__class__. Теперь у вас есть переменная c привязан к классу, хотя он, вероятно, не имеет того же имени, что и класс. Вы можно использовать это для доступа к атрибутам класса или даже вызвать его, чтобы создать больше экземпляров (даже если вы не знаете, что это за класс!).

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

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

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

в вашем примере 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. Объект