Хорошо, обо всем по порядку.
В Python нет таких вещей, как «объявление переменной» или «инициализация переменной».
Это просто то, что мы называем «присваиванием», но, вероятно, следует называть просто «именование».
Присваивание означает, что «это имя в левой части теперь относится к результату оценки правой части, независимо от того, на что оно ссылалось раньше (если что-либо)».
foo = 'bar'
foo = 2 * 3
Таким образом, имена Python (возможно, более подходящий термин, чем «переменные») не имеют связанных типов; ценности делают. Вы можете повторно применить одно и то же имя ко всему, независимо от его типа, но поведение объекта по-прежнему зависит от его типа. Имя - это просто способ обозначить значение (объект). Это отвечает на ваш второй вопрос: вы не создаете переменные для хранения пользовательского типа. Вы не создаете переменные для хранения какого-либо определенного типа. Вы вообще не «создаете» переменные. Вы даете имена объектам.
Второй момент: когда дело доходит до классов, Python следует очень простому правилу, которое на самом деле гораздо более согласованно, чем то, что делают такие языки, как Java, C ++ и C #: все, что объявлено внутри class
блока, является частью класса . Итак, def
написанные здесь функции ( ) являются методами, то есть частью объекта класса (не сохраняются для каждого экземпляра), как в Java, C ++ и C #; но другие имена здесь также являются частью класса. Опять же, имена - это просто имена, и они не имеют связанных типов, а функции также являются объектами в Python. Таким образом:
class Example:
data = 42
def method(self): pass
В Python классы тоже являются объектами .
Итак, теперь мы создали объект с именем Example
, который представляет класс всех вещей, которые есть Example
. Этот объект имеет два атрибута, задаваемых пользователем (в C ++ - «члены»; в C # - «поля или свойства или методы»; в Java - «поля или методы»). Один из них назван data
, и в нем хранится целочисленное значение 42
. Другой назван method
, и он хранит объект функции. (Есть еще несколько атрибутов, которые Python добавляет автоматически.)
Однако эти атрибуты по-прежнему не являются частью объекта. По сути, объект - это просто набор других имен (имен атрибутов), пока вы не перейдете к вещам, которые больше нельзя разделить. Таким образом, значения могут совместно использоваться разными экземплярами класса или даже между объектами разных классов, если вы намеренно это настроили.
Создадим экземпляр:
x = Example()
Теперь у нас есть отдельный объект с именем x
, который является экземпляром Example
. На самом деле data
и method
не являются частью объекта, но мы все равно можем искать их с помощью x
некоторой магии, которую Python делает за кулисами. В method
частности, когда мы смотрим вверх , мы вместо этого получим «связанный метод» (когда мы его вызываем, он x
автоматически передается в качестве self
параметра, чего не может произойти, если мы посмотрим Example.method
прямо вверх ).
Что происходит, когда мы пытаемся использовать x.data
?
Когда мы исследуем его, он сначала ищет объект. Если его нет в объекте, Python просматривает класс.
Однако, когда мы назначаем x.data
, Python создаст атрибут для объекта. Он не заменит атрибут класса.
Это позволяет нам выполнять инициализацию объекта . Python автоматически вызовет метод класса для __init__
новых экземпляров при их создании, если они есть. В этом методе мы можем просто назначить атрибуты, чтобы установить начальные значения для этого атрибута для каждого объекта:
class Example:
name = "Ignored"
def __init__(self, name):
self.name = name
Теперь мы должны указать a name
при создании Example
, и каждый экземпляр имеет свой собственный name
. Python будет игнорировать атрибут класса Example.name
всякий раз, когда мы ищем .name
экземпляр экземпляра, потому что атрибут экземпляра будет найден первым.
И последнее предостережение: модификация (мутация) и присвоение - разные вещи!
В Python строки неизменяемы. Их нельзя изменить. Когда вы это сделаете:
a = 'hi '
b = a
a += 'mom'
Вы не меняете исходную строку «привет». В Python это невозможно. Вместо этого вы создаете новую строку 'hi mom'
и заставляете a
перестать быть именем 'hi '
, а 'hi mom'
вместо этого начинаете быть именем . Мы также создали b
имя 'hi '
, и после повторного применения a
имя b
все еще остается именем 'hi '
, потому что 'hi '
все еще существует и не было изменено.
Но списки можно изменить:
a = [1, 2, 3]
b = a
a += [4]
Теперь b
это [1, 2, 3, 4], потому что мы создали b
имя для той же вещи, что и a
название, а затем мы изменили это. Мы не создавали новый список для a
имени, потому что Python просто по- +=
другому относится к спискам.
Это важно для объектов, потому что если бы у вас был список как атрибут класса и вы использовали экземпляр для изменения списка, то изменение было бы «видно» во всех других экземплярах. Это потому, что (а) данные на самом деле являются частью объекта класса, а не объекта-экземпляра; (b) поскольку вы изменяли список, а не выполняли простое присвоение, вы не создали новый атрибут экземпляра, скрывающий атрибут класса.