HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-26
1 <h2>Экземпляры</h2>
1 <h2>Экземпляры</h2>
2 <p>Класс, как мы уже увидели, может хранить данные. Но типичный класс присутствует в программе в единственном экземпляре. Поэтому сам по себе класс не очень полезен, ведь хранить определения можно и в модулях. Весь смысл использования классов заключается в их<em>инстанцировании</em>.</p>
2 <p>Класс, как мы уже увидели, может хранить данные. Но типичный класс присутствует в программе в единственном экземпляре. Поэтому сам по себе класс не очень полезен, ведь хранить определения можно и в модулях. Весь смысл использования классов заключается в их<em>инстанцировании</em>.</p>
3 <p><em>Инстанцированием (instantiation)</em>называют создание на основе класса<em>экземпляра (instance)</em>- такого объекта, который получает доступ ко всему содержимому класса, но при этом обладает и способностью хранить собственные данные. При этом, имея объект, всегда можно узнать, экземпляром какого класса он является.</p>
3 <p><em>Инстанцированием (instantiation)</em>называют создание на основе класса<em>экземпляра (instance)</em>- такого объекта, который получает доступ ко всему содержимому класса, но при этом обладает и способностью хранить собственные данные. При этом, имея объект, всегда можно узнать, экземпляром какого класса он является.</p>
4 <p>Давайте объявим класс и создадим пару экземпляров, а заодно и познакомимся с синтаксисом инстанцирования классов:</p>
4 <p>Давайте объявим класс и создадим пару экземпляров, а заодно и познакомимся с синтаксисом инстанцирования классов:</p>
5 <p>Что мы можем увидеть в этом примере? Первое, что бросается в глаза, это вызов класса как функции: Person(). Сходство это - не только внешнее. В Python инстанцирование фактически и является вызовом некоторой функции, которая возвращает<em>новый экземпляр</em>класса.</p>
5 <p>Что мы можем увидеть в этом примере? Первое, что бросается в глаза, это вызов класса как функции: Person(). Сходство это - не только внешнее. В Python инстанцирование фактически и является вызовом некоторой функции, которая возвращает<em>новый экземпляр</em>класса.</p>
6 <blockquote><p>При выводе объекта класса в REPL можно увидеть строку, похожую на вывод информации о классе, только вместо "class" в строчке упоминается "object".</p>
6 <blockquote><p>При выводе объекта класса в REPL можно увидеть строку, похожую на вывод информации о классе, только вместо "class" в строчке упоминается "object".</p>
7 </blockquote><p>Также стоит обратить внимание на то, что все экземпляры являются отдельными объектами, поэтому оператор is дает False как при соотнесении экземпляров между собой, так и при соотнесении любого экземпляра с объектом класса (bob, alice и Person - три самостоятельных объекта).</p>
7 </blockquote><p>Также стоит обратить внимание на то, что все экземпляры являются отдельными объектами, поэтому оператор is дает False как при соотнесении экземпляров между собой, так и при соотнесении любого экземпляра с объектом класса (bob, alice и Person - три самостоятельных объекта).</p>
8 <h2>Атрибуты класса и экземпляры</h2>
8 <h2>Атрибуты класса и экземпляры</h2>
9 <p>В предыдущем примере класс был пустой. Теперь воспроизведем его, но добавим на этот раз атрибут:</p>
9 <p>В предыдущем примере класс был пустой. Теперь воспроизведем его, но добавим на этот раз атрибут:</p>
10 <p>Этот пример показывает, что bob, и alice имеют атрибут name, и что значение атрибутов name - общее для всех трех объектов.</p>
10 <p>Этот пример показывает, что bob, и alice имеют атрибут name, и что значение атрибутов name - общее для всех трех объектов.</p>
11 <p>Давайте же переименуем Боба:</p>
11 <p>Давайте же переименуем Боба:</p>
12 <p>Вот вы и увидели то самое "собственное состояние объекта". Person продолжает давать имя всем экземплярам,<em>пока те не изменят</em>значение своего атрибута. В момент присваивания нового значения атрибуту экземпляра, экземпляр получает<em>свой собственный атрибут</em>.</p>
12 <p>Вот вы и увидели то самое "собственное состояние объекта". Person продолжает давать имя всем экземплярам,<em>пока те не изменят</em>значение своего атрибута. В момент присваивания нового значения атрибуту экземпляра, экземпляр получает<em>свой собственный атрибут</em>.</p>
13 <h2>Атрибут __dict__</h2>
13 <h2>Атрибут __dict__</h2>
14 <p>Стоит прямо сейчас заглянуть "под капот" объектной системы Python, чтобы вы в дальнейшем могли исследовать объекты самостоятельно. Это и интересно, и полезно - как при обучении, так и при отладке объектного кода.</p>
14 <p>Стоит прямо сейчас заглянуть "под капот" объектной системы Python, чтобы вы в дальнейшем могли исследовать объекты самостоятельно. Это и интересно, и полезно - как при обучении, так и при отладке объектного кода.</p>
15 <p>Итак, внутри каждого объекта Python хранит…<em>словарь</em>. Имена атрибутов в пространствах имен выступают ключами этого словаря, а значения являются ссылками на другие объекты. Словарь этот всегда называется __dict__ и тоже является атрибутом. Обращаясь к этому словарю, вы можете получить доступ к значениям атрибутов:</p>
15 <p>Итак, внутри каждого объекта Python хранит…<em>словарь</em>. Имена атрибутов в пространствах имен выступают ключами этого словаря, а значения являются ссылками на другие объекты. Словарь этот всегда называется __dict__ и тоже является атрибутом. Обращаясь к этому словарю, вы можете получить доступ к значениям атрибутов:</p>
16 <p>Присмотритесь, и вы увидите: у bob в __dict__ есть его собственное имя, а у alice собственного имени нет. Но при обращении к атрибуту привычным способом "через точку", вы видите имя и у alice. Как же это работает?</p>
16 <p>Присмотритесь, и вы увидите: у bob в __dict__ есть его собственное имя, а у alice собственного имени нет. Но при обращении к атрибуту привычным способом "через точку", вы видите имя и у alice. Как же это работает?</p>
17 <p>Дело в том, что Python при обращении к атрибуту сначала ищет атрибут в словаре экземпляра. Но если там соответствующего ключа не нашлось, то атрибут ищется уже в классе. Именно так alice получает имя - Python находит его в классе Person.</p>
17 <p>Дело в том, что Python при обращении к атрибуту сначала ищет атрибут в словаре экземпляра. Но если там соответствующего ключа не нашлось, то атрибут ищется уже в классе. Именно так alice получает имя - Python находит его в классе Person.</p>
18 <p>Надо сказать, что это очень разумный подход. Да, Python мог бы копировать словарь класса при инстанцировании. Но это привело бы к излишнему потреблению памяти. А вот "коллективное использование", напротив, позволяет память экономить.</p>
18 <p>Надо сказать, что это очень разумный подход. Да, Python мог бы копировать словарь класса при инстанцировании. Но это привело бы к излишнему потреблению памяти. А вот "коллективное использование", напротив, позволяет память экономить.</p>
19 <p>И, конечно же, словарь __dict__ объекта может быть изменен. Когда мы давали Бобу имя, мы на самом деле сделали что-то такое:</p>
19 <p>И, конечно же, словарь __dict__ объекта может быть изменен. Когда мы давали Бобу имя, мы на самом деле сделали что-то такое:</p>
20 <p>Мы даже можем добавить Бобу фамилию и сделать это через модификацию __dict__:</p>
20 <p>Мы даже можем добавить Бобу фамилию и сделать это через модификацию __dict__:</p>
21 <p>А ведь у класса не было атрибута surname. Каждый экземпляр класса тоже является самостоятельным пространством имен, пригодным для расширения в процессе исполнения программы. За счет использования под капотом словарей, как вы теперь знаете.</p>
21 <p>А ведь у класса не было атрибута surname. Каждый экземпляр класса тоже является самостоятельным пространством имен, пригодным для расширения в процессе исполнения программы. За счет использования под капотом словарей, как вы теперь знаете.</p>
22 <h2>Проверка принадлежности экземпляра к классу</h2>
22 <h2>Проверка принадлежности экземпляра к классу</h2>
23 <p>Выше мы уже упоминали, что объект всегда связан с классом. Эта связь заключается в наличии у экземпляра атрибута __class__, который является ссылкой на объект класса:</p>
23 <p>Выше мы уже упоминали, что объект всегда связан с классом. Эта связь заключается в наличии у экземпляра атрибута __class__, который является ссылкой на объект класса:</p>
24 <p>Как вы уже могли заметить, в Python многие "внутренние штуки" имеют имена, заключенные в двойные символы подчеркивания. В разговоре питонисты обычно проговаривают подобные имена примерно так: "дАндер-класс", что является калькой с "dunder class", где "dunder", в свою очередь, это сокращение от "double underscore", то есть "двойной символ подчеркивания". Полезно запомнить этот стиль именования.</p>
24 <p>Как вы уже могли заметить, в Python многие "внутренние штуки" имеют имена, заключенные в двойные символы подчеркивания. В разговоре питонисты обычно проговаривают подобные имена примерно так: "дАндер-класс", что является калькой с "dunder class", где "dunder", в свою очередь, это сокращение от "double underscore", то есть "двойной символ подчеркивания". Полезно запомнить этот стиль именования.</p>
25 <p>А еще стоит запомнить, что практически всегда, когда вы хотите использовать что-то, названное в dunder-стиле, "есть способ лучше". Так с __dict__ напрямую работать не приходится, потому что есть возможность обращаться к атрибутам "через точку". Вот и __class__ в коде встречается редко. А<em>рекомендуемый способ</em>проверки принадлежности к классу выглядит так:</p>
25 <p>А еще стоит запомнить, что практически всегда, когда вы хотите использовать что-то, названное в dunder-стиле, "есть способ лучше". Так с __dict__ напрямую работать не приходится, потому что есть возможность обращаться к атрибутам "через точку". Вот и __class__ в коде встречается редко. А<em>рекомендуемый способ</em>проверки принадлежности к классу выглядит так:</p>
26 <p>Запомните эту функцию. Она "умнее", чем вам может сейчас показаться. В дальнейшем мы увидим более интересные примеры ее применения.</p>
26 <p>Запомните эту функцию. Она "умнее", чем вам может сейчас показаться. В дальнейшем мы увидим более интересные примеры ее применения.</p>