Гаразд, спочатку про все.
У 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
s. Цей об'єкт має два атрибути, надані користувачем (у 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
Тепер ми повинні вказати, name
коли ми створюємо Example
, і кожен екземпляр має своє name
. Python буде ігнорувати атрибут class 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 просто по- +=
різному трактує списки.
Це має значення для об’єктів, оскільки якби у вас був список як атрибут класу та ви використовували екземпляр для модифікації списку, тоді зміна буде «видно» у всіх інших випадках. Це пов’язано з тим, що (а) дані насправді є частиною об’єкта класу, а не будь-яким об’єктом екземпляра; (б) оскільки ви модифікували список і не виконували просте призначення, ви не створили новий атрибут екземпляра, що приховує атрибут класу.