classes.md
1> **Источник:** https://python-all.ru/2.7/tutorial/classes.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 9. Классы89По сравнению с другими языками программирования, механизм классов Python добавляет классы с минимумом нового синтаксиса и семантики. Это смесь механизмов классов, применяемых в C++ и Modula-3. Классы Python предоставляют все стандартные возможности объектно-ориентированного программирования: механизм наследования классов допускает множественные базовые классы, производный класс может переопределять любые методы своего базового класса или классов, и метод может вызывать метод базового класса с тем же именем. Объекты могут содержать произвольное количество и виды данных. Как и модули, классы обладают динамической природой Python: они создаются во время выполнения и могут быть изменены после создания.1011В терминологии C++ члены класса (включая элементы данных) обычно являются *открытыми* (исключение см. ниже [Приватные переменные и локальные ссылки классов](https://python-all.ru/2.7/tutorial/classes.html#tut-private)), а все функции-члены – *виртуальными*. Как и в Modula-3, нет сокращённых способов обращения к членам объекта из его методов: функция-метод объявляется с явным первым аргументом, представляющим объект, который неявно передаётся при вызове. Как и в Smalltalk, классы сами являются объектами. Это даёт семантику для импорта и переименования. В отличие от C++ и Modula-3, встроенные типы можно использовать как базовые классы для расширения пользователем. Также, как и в C++, большинство встроенных операторов со специальным синтаксисом (арифметические операторы, индексация и т.д.) можно переопределять для экземпляров классов.1213(Из-за отсутствия общепринятой терминологии для описания классов, я буду иногда использовать термины Smalltalk и C++. Я бы использовал термины Modula-3, поскольку его объектно-ориентированная семантика ближе к Python, чем к C++, но, полагаю, немногие читатели слышали о нём.)1415## 9.1. Несколько слов об именах и объектах1617Объекты обладают индивидуальностью, и несколько имён (в разных областях видимости) могут быть связаны с одним и тем же объектом. В других языках это называется псевдонимизацией (aliasing). При первом знакомстве с Python на это обычно не обращают внимания, и это можно безопасно игнорировать при работе с неизменяемыми базовыми типами (числа, строки, кортежи). Однако псевдонимизация может неожиданно повлиять на семантику кода Python, использующего изменяемые объекты, такие как списки, словари и большинство других типов. Обычно это используется на пользу программе, поскольку псевдонимы в некоторых отношениях ведут себя как указатели. Например, передача объекта – дешёвая операция, так как реализация передаёт только указатель; и если функция изменяет объект, переданный как аргумент, вызывающий увидит изменения – это устраняет необходимость в двух разных механизмах передачи аргументов, как в Pascal.1819## 9.2. Области видимости и пространства имён Python2021Прежде чем ввести классы, я должен рассказать кое-что о правилах областей видимости в Python. Определения классов используют некоторые хитрости с пространствами имён, и нужно знать, как работают области видимости и пространства имён, чтобы полностью понимать происходящее. Кстати, знание этого предмета полезно любому продвинутому программисту на Python.2223Начнём с некоторых определений.2425*Пространство имён* – это отображение имён на объекты. Большинство пространств имён в настоящее время реализованы как словари Python, но это обычно никак не заметно (кроме производительности) и может измениться в будущем. Примеры пространств имён: множество встроенных имён (содержащее такие функции, как [`abs()`](https://python-all.ru/2.7/library/functions.html#abs), и имена встроенных исключений); глобальные имена в модуле; локальные имена в вызове функции. В некотором смысле набор атрибутов объекта также образует пространство имён. Важно знать, что между именами в разных пространствах имён нет абсолютно никакой связи; например, два разных модуля могут оба определять функцию `maximize` без путаницы – пользователи модулей должны указывать перед ней имя модуля.2627Кстати, я использую слово *атрибут* для любого имени, следующего за точкой – например, в выражении `z.real`, `real` является атрибутом объекта `z`. Строго говоря, ссылки на имена в модулях являются ссылками на атрибуты: в выражении `modname.funcname`, `modname` – это объект модуля, а `funcname` – его атрибут. В этом случае существует прямое соответствие между атрибутами модуля и глобальными именами, определёнными в модуле: они используют одно и то же пространство имён! [1](https://python-all.ru/2.7/tutorial/classes.html#id2)2829Атрибуты могут быть доступны только для чтения или для записи. В последнем случае возможно присваивание атрибутам. Атрибуты модуля доступны для записи: можно написать `modname.the_answer = 42`. Атрибуты, доступные для записи, также можно удалить с помощью оператора [`del`](https://python-all.ru/2.7/reference/simple_stmts.html#del). Например, `del modname.the_answer` удалит атрибут `the_answer` из объекта, обозначенного именем `modname`.3031Пространства имён создаются в разные моменты и имеют разное время жизни. Пространство имён, содержащее встроенные имена, создаётся при запуске интерпретатора Python и никогда не удаляется. Глобальное пространство имён для модуля создаётся при чтении определения модуля; обычно пространства имён модулей существуют до завершения интерпретатора. Операторы, выполняемые при верхнеуровневом вызове интерпретатора (из скрипта или в интерактивном режиме), считаются частью модуля с именем [`__main__`](https://python-all.ru/2.7/library/__main__.html#module-__main__), поэтому у них есть собственное глобальное пространство имён. (На самом деле встроенные имена также живут в модуле; он называется [`__builtin__`](https://python-all.ru/2.7/library/__builtin__.html#module-__builtin__).)3233Локальное пространство имён для функции создаётся при её вызове и удаляется, когда функция завершается или возбуждает исключение, не обработанное внутри функции. (На самом деле, «забывание» было бы лучшим способом описать происходящее.) Конечно, рекурсивные вызовы имеют собственные локальные пространства имён.3435*Область видимости* – это текстовая область программы на Python, в которой пространство имён доступно напрямую. «Напрямую доступно» означает, что неквалифицированная ссылка на имя пытается найти это имя в пространстве имён.3637Хотя области видимости определяются статически, они используются динамически. В любой момент выполнения доступны как минимум три вложенные области видимости, чьи пространства имён непосредственно доступны:3839- самая внутренняя область, которая просматривается первой, содержит локальные имена40- области видимости любых внешних функций, которые ищутся начиная с ближайшей внешней области, содержат имена, не являющиеся локальными, но и не глобальными41- предпоследняя область содержит глобальные имена текущего модуля42- самая внешняя область (просматривается последней) – это пространство имён, содержащее встроенные имена4344Если имя объявлено как global, то все ссылки и присваивания напрямую относятся к промежуточной области видимости, содержащей глобальные имена модуля. В противном случае все переменные, найденные за пределами самой внутренней области видимости, доступны только для чтения (попытка записи в такую переменную просто создаст *новую* локальную переменную в самой внутренней области видимости, оставив внешнюю переменную с тем же именем без изменений).4546Обычно локальная область видимости ссылается на локальные имена (текстуально) текущей функции. Вне функций локальная область видимости ссылается на то же пространство имён, что и глобальная: пространство имён модуля. Определения классов помещают ещё одно пространство имён в локальную область.4748Важно понимать, что области видимости определяются текстуально: глобальная область функции, определённой в модуле, – это пространство имён этого модуля, независимо от того, откуда и под каким псевдонимом функция вызывается. С другой стороны, фактический поиск имён выполняется динамически, во время выполнения – однако определение языка развивается в сторону статического разрешения имён, во время «компиляции», так что не полагайтесь на динамическое разрешение имён! (На самом деле, локальные переменные уже определяются статически.)4950Особенность Python в том, что при отсутствии действующего оператора [`global`](https://python-all.ru/2.7/reference/simple_stmts.html#global) присваивание имён всегда происходит в самой внутренней области видимости. Присваивания не копируют данные – они лишь связывают имена с объектами. То же верно для удаления: оператор `del x` удаляет привязку `x` из пространства имён, на которое ссылается локальная область видимости. Фактически, все операции, вводящие новые имена, используют локальную область видимости: в частности, операторы [`import`](https://python-all.ru/2.7/reference/simple_stmts.html#import) и определения функций связывают имя модуля или функции в локальной области видимости. (Оператор [`global`](https://python-all.ru/2.7/reference/simple_stmts.html#global) можно использовать, чтобы указать, что определённые переменные находятся в глобальной области видимости.)5152## 9.3. Первое знакомство с классами5354Классы вводят немного нового синтаксиса, три новых типа объектов и некоторую новую семантику.5556### 9.3.1. Синтаксис определения класса5758Простейшая форма определения класса выглядит так:5960```python61class ClassName:62 <statement-1>63 .64 .65 .66 <statement-N>67```6869Определения классов, как и определения функций (операторы [`def`](https://python-all.ru/2.7/reference/compound_stmts.html#def)), должны быть выполнены, чтобы возыметь действие. (Теоретически можно разместить определение класса в ветке оператора [`if`](https://python-all.ru/2.7/reference/compound_stmts.html#if) или внутри функции.)7071На практике операторы внутри определения класса обычно являются определениями функций, но допускаются и другие операторы, и иногда они полезны – мы вернёмся к этому позже. Определения функций внутри класса обычно имеют особую форму списка аргументов, продиктованную соглашениями о вызове методов – и это тоже объясняется позже.7273При входе в определение класса создаётся новое пространство имён, которое используется как локальная область видимости – таким образом, все присваивания локальным переменным попадают в это новое пространство имён. В частности, определения функций связывают имя новой функции здесь.7475Когда определение класса завершается нормально (через конец), создаётся *объект класса*. Это по сути обёртка вокруг содержимого пространства имён, созданного определением класса; мы узнаем больше об объектах класса в следующем разделе. Исходная локальная область видимости (действовавшая непосредственно перед входом в определение класса) восстанавливается, и объект класса связывается здесь с именем класса, указанным в заголовке определения класса (`ClassName` в примере).7677### 9.3.2. Объекты классов7879Объекты классов поддерживают два вида операций: обращение к атрибутам и создание экземпляров.8081*Обращение к атрибутам* использует стандартный синтаксис, принятый для всех обращений к атрибутам в Python: `obj.name`. Допустимыми именами атрибутов являются все имена, которые находились в пространстве имён класса на момент создания объекта класса. Итак, если определение класса выглядело так:8283```python84class MyClass:85 """Простой пример класса"""86 i = 123458788 def f(self):89 return 'hello world'90```9192тогда `MyClass.i` и `MyClass.f` являются допустимыми ссылками на атрибуты, возвращающими целое число и объект функции соответственно. Атрибутам класса также можно присваивать значения, поэтому можно изменить значение `MyClass.i` путём присваивания. `__doc__` также является допустимым атрибутом, возвращающим строку документации, принадлежащую классу: `"A simple example class"`.9394*Создание экземпляра* класса использует синтаксис вызова функции. Просто представьте, что объект класса – это функция без параметров, возвращающая новый экземпляр класса. Например (предполагая приведённый выше класс):9596```python97x = MyClass()98```99100создаёт новый *экземпляр* класса и присваивает этот объект локальной переменной `x`.101102Операция создания экземпляра («вызов» объекта класса) создаёт пустой объект. Многие классы предпочитают создавать объекты с экземплярами, настроенными на определённое начальное состояние. Поэтому класс может определить специальный метод с именем [`__init__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__init__), например так:103104```python105def __init__(self):106 self.data = []107```108109Когда класс определяет метод [`__init__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__init__), создание экземпляра класса автоматически вызывает [`__init__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__init__) для только что созданного экземпляра класса. Таким образом, в этом примере новый инициализированный экземпляр можно получить с помощью:110111```python112x = MyClass()113```114115Конечно, метод [`__init__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__init__) может иметь аргументы для большей гибкости. В этом случае аргументы, переданные оператору создания экземпляра класса, передаются [`__init__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__init__). Например,116117```python118>>> class Complex:119... def __init__(self, realpart, imagpart):120... self.r = realpart121... self.i = imagpart122...123>>> x = Complex(3.0, -4.5)124>>> x.r, x.i125(3.0, -4.5)126```127128### 9.3.3. Объекты экземпляров129130Что можно делать с объектами экземпляров? Единственные операции, поддерживаемые объектами экземпляров, – это ссылки на атрибуты. Существует два вида допустимых имён атрибутов: атрибуты-данные и методы.131132*Атрибуты данных* соответствуют «переменным экземпляра» в Smalltalk и «членам данных» в C++. Атрибуты данных не нужно объявлять; как и локальные переменные, они возникают при первом присваивании. Например, если `x` является экземпляром `MyClass`, созданным выше, следующий фрагмент кода выведет значение `16`, не оставив следа:133134```python135x.counter = 1136while x.counter < 10:137 x.counter = x.counter * 2138print x.counter139del x.counter140```141142Другой вид ссылки на атрибут экземпляра – это *метод*. Метод – это функция, которая «принадлежит» объекту. (В Python термин «метод» не уникален для экземпляров классов: другие типы объектов также могут иметь методы. Например, объекты списка имеют методы append, insert, remove, sort и так далее. Однако в дальнейшем обсуждении мы будем использовать термин «метод» исключительно для обозначения методов экземпляров классов, если явно не указано иное.)143144Допустимые имена методов объекта экземпляра зависят от его класса. По определению, все атрибуты класса, являющиеся объектами-функциями, определяют соответствующие методы его экземпляров. Так, в нашем примере `x.f` является допустимой ссылкой на метод, так как `MyClass.f` – функция, а `x.i` – нет, поскольку `MyClass.i` функцией не является. Но `x.f` – это не то же самое, что `MyClass.f`; это *объект метода*, а не объект функции.145146### 9.3.4. Объекты методов147148Обычно метод вызывается сразу после его связывания:149150```python151x.f()152```153154В примере `MyClass` это вернёт строку `'hello world'`. Однако необязательно вызывать метод сразу: `x.f` – это объект-метод, который можно сохранить и вызвать позже. Например:155156```python157xf = x.f158while True:159 print xf()160```161162будет продолжать печатать `hello world` до бесконечности.163164Что именно происходит при вызове метода? Возможно, вы заметили, что `x.f()` был вызван без аргумента выше, хотя определение функции для `f()` указывало аргумент. Что случилось с аргументом? Разумеется, Python возбуждает исключение, когда функция, требующая аргумент, вызывается без него – даже если этот аргумент фактически не используется…165166На самом деле ответ можно было угадать: особенность методов в том, что объект передаётся как первый аргумент функции. В нашем примере вызов `x.f()` в точности эквивалентен `MyClass.f(x)`. В общем случае вызов метода со списком из *n* аргументов эквивалентен вызову соответствующей функции со списком аргументов, который создаётся путём вставки объекта метода перед первым аргументом.167168Если всё ещё неясно, как работают методы, взгляд на реализацию может прояснить ситуацию. Когда происходит обращение к атрибуту экземпляра, не являющемуся данными, выполняется поиск в классе этого экземпляра. Если имя соответствует допустимому атрибуту класса, который является объектом функции, то создаётся объект метода путём упаковки (указателей на) экземпляр и найденную функцию в абстрактный объект: это и есть объект метода. Когда объект метода вызывается со списком аргументов, создаётся новый список аргументов из экземпляра и исходного списка, и функция вызывается с этим новым списком аргументов.169170### 9.3.5. Переменные класса и экземпляра171172Вообще говоря, переменные экземпляра предназначены для данных, уникальных для каждого экземпляра, а переменные класса – для атрибутов и методов, общих для всех экземпляров класса:173174```python175class Dog:176177 kind = 'canine' # переменная класса, общая для всех экземпляров178179 def __init__(self, name):180 self.name = name # переменная экземпляра, уникальная для каждого экземпляра181182>>> d = Dog('Fido')183>>> e = Dog('Buddy')184>>> d.kind # общая для всех собак185'canine'186>>> e.kind # общая для всех собак187'canine'188>>> d.name # уникально для d189'Fido'190>>> e.name # уникально для e191'Buddy'192```193194Как обсуждалось в [Замечание об именах и объектах](https://python-all.ru/2.7/tutorial/classes.html#tut-object), совместно используемые данные могут иметь неожиданные последствия при работе с [изменяемыми](https://python-all.ru/2.7/glossary.html#term-mutable) объектами, такими как списки и словари. Например, список *tricks* в следующем коде не следует использовать как переменную класса, поскольку этот единственный список будет общим для всех *Dog* экземпляров:195196```python197class Dog:198199 tricks = [] # ошибочное использование переменной класса200201 def __init__(self, name):202 self.name = name203204 def add_trick(self, trick):205 self.tricks.append(trick)206207>>> d = Dog('Fido')208>>> e = Dog('Buddy')209>>> d.add_trick('roll over')210>>> e.add_trick('play dead')211>>> d.tricks # неожиданно общая для всех собак212['roll over', 'play dead']213```214215Правильное проектирование класса подразумевает использование переменной экземпляра:216217```python218class Dog:219220 def __init__(self, name):221 self.name = name222 self.tricks = [] # создаёт новый пустой список для каждой собаки223224 def add_trick(self, trick):225 self.tricks.append(trick)226227>>> d = Dog('Fido')228>>> e = Dog('Buddy')229>>> d.add_trick('roll over')230>>> e.add_trick('play dead')231>>> d.tricks232['roll over']233>>> e.tricks234['play dead']235```236237## 9.4. Разные замечания238239Атрибуты данных переопределяют одноимённые атрибуты методов; чтобы избежать случайных конфликтов имён, которые могут вызвать труднонаходимые ошибки в больших программах, разумно использовать какое-либо соглашение, минимизирующее вероятность конфликтов. Возможные соглашения включают написание имён методов с заглавной буквы, добавление префикса к именам атрибутов данных в виде короткой уникальной строки (возможно, просто подчёркивания) или использование глаголов для методов и существительных для атрибутов данных.240241Атрибуты данных могут использоваться как методами, так и обычными пользователями («клиентами») объекта. Иными словами, классы не пригодны для реализации чистых абстрактных типов данных. На самом деле в Python нет возможности принудительно скрыть данные – всё строится на соглашениях. (С другой стороны, реализация Python, написанная на C, может полностью скрыть детали реализации и контролировать доступ к объекту, если это необходимо; эта возможность может использоваться расширениями Python, написанными на C.)242243Клиенты должны осторожно обращаться с атрибутами данных – они могут нарушить инварианты, поддерживаемые методами, если будут записывать свои значения в эти атрибуты. Обратите внимание, что клиенты могут добавлять собственные атрибуты данных к объекту экземпляра, не влияя на корректность методов, при условии отсутствия конфликтов имён – и снова соглашение об именах может избавить от многих проблем.244245Не существует краткой формы для обращения к атрибутам данных (или другим методам!) из методов. Я нахожу, что это на самом деле повышает читаемость методов: при беглом просмотре метода невозможно перепутать локальные переменные и переменные экземпляра.246247Часто первый аргумент метода называется `self`. Это не более чем соглашение: имя `self` не имеет для Python абсолютно никакого особого значения. Однако имейте в виду, что, не следуя этому соглашению, вы делаете свой код менее читаемым для других программистов Python; также возможно, что будет написана программа *обозреватель классов*, которая полагается на такое соглашение.248249Любой объект функции, являющийся атрибутом класса, определяет метод для экземпляров этого класса. Совершенно необязательно, чтобы определение функции было текстуально вложено в определение класса: можно также присвоить объект функции локальной переменной в классе. Пример:250251```python252# Функция, определённая вне класса253def f1(self, x, y):254 return min(x, x+y)255256class C:257 f = f1258259 def g(self):260 return 'hello world'261262 h = g263```264265Теперь `f`, `g` и `h` – все атрибуты класса `C`, которые ссылаются на объекты функций, и, следовательно, все они являются методами экземпляров `C` – `h` полностью эквивалентен `g`. Обратите внимание, что такая практика обычно только запутывает читателя программы.266267Методы могут вызывать другие методы, используя атрибуты метода аргумента `self`:268269```python270class Bag:271 def __init__(self):272 self.data = []273274 def add(self, x):275 self.data.append(x)276277 def addtwice(self, x):278 self.add(x)279 self.add(x)280```281282Методы могут ссылаться на глобальные имена так же, как обычные функции. Глобальная область видимости, связанная с методом, – это модуль, содержащий его определение. (Класс никогда не используется в качестве глобальной области видимости.) Хотя редко встречаются веские причины для использования глобальных данных в методе, существуют многие законные применения глобальной области видимости: например, функции и модули, импортированные в глобальную область видимости, могут использоваться методами, а также функции и классы, определённые в ней. Обычно класс, содержащий метод, сам определён в этой глобальной области видимости, и в следующем разделе мы найдём несколько хороших причин, по которым методу может понадобиться ссылаться на собственный класс.283284Каждое значение является объектом и поэтому имеет *класс* (также называемый его *типом*). Он хранится как `object.__class__`.285286## 9.5. Наследование287288Разумеется, языковая возможность не заслуживала бы названия «класс», если бы не поддерживала наследование. Синтаксис определения производного класса выглядит так:289290```python291class DerivedClassName(BaseClassName):292 <statement-1>293 .294 .295 .296 <statement-N>297```298299Имя `BaseClassName` должно быть определено в области видимости, содержащей определение производного класса. Вместо имени базового класса также допускаются другие произвольные выражения. Это может быть полезно, например, когда базовый класс определён в другом модуле:300301```python302class DerivedClassName(modname.BaseClassName):303```304305Выполнение определения производного класса происходит так же, как и для базового класса. При создании объекта класса базовый класс запоминается. Это используется для разрешения ссылок на атрибуты: если запрашиваемый атрибут не найден в классе, поиск продолжается в базовом классе. Это правило применяется рекурсивно, если сам базовый класс является производным от какого-либо другого класса.306307В создании экземпляров производных классов нет ничего особенного: `DerivedClassName()` создаёт новый экземпляр класса. Ссылки на методы разрешаются следующим образом: выполняется поиск соответствующего атрибута класса, при необходимости спускаясь по цепочке базовых классов; ссылка на метод считается действительной, если результатом поиска является объект функции.308309Производные классы могут переопределять методы своих базовых классов. Поскольку методы не имеют особых привилегий при вызове других методов того же объекта, метод базового класса, который вызывает другой метод, определённый в том же базовом классе, может в итоге вызвать метод производного класса, переопределяющий его. (Для программистов на C++: все методы в Python по сути являются `virtual`.)310311Переопределяющий метод в производном классе может на самом деле захотеть расширить, а не просто заменить метод базового класса с тем же именем. Есть простой способ напрямую вызвать метод базового класса: просто вызовите `BaseClassName.methodname(self, arguments)`. Это иногда полезно и для клиентов. (Обратите внимание, что это работает только в том случае, если базовый класс доступен как `BaseClassName` в глобальной области видимости.)312313В Python есть две встроенные функции, работающие с наследованием:314315- Используйте [`isinstance()`](https://python-all.ru/2.7/library/functions.html#isinstance) для проверки типа экземпляра: `isinstance(obj, int)` будет `True` только если `obj.__class__` является [`int`](https://python-all.ru/2.7/library/functions.html#int) или какого-либо класса, производного от [`int`](https://python-all.ru/2.7/library/functions.html#int).316- Для проверки наследования классов используется [`issubclass()`](https://python-all.ru/2.7/library/functions.html#issubclass): `issubclass(bool, int)` равно `True`, так как [`bool`](https://python-all.ru/2.7/library/functions.html#bool) – подкласс [`int`](https://python-all.ru/2.7/library/functions.html#int). Однако `issubclass(unicode, str)` равно `False`, поскольку [`unicode`](https://python-all.ru/2.7/library/functions.html#unicode) не является подклассом [`str`](https://python-all.ru/2.7/library/functions.html#str) (у них только общий предок – [`basestring`](https://python-all.ru/2.7/library/functions.html#basestring)).317318### 9.5.1. Множественное наследование319320Python также поддерживает ограниченную форму множественного наследования. Определение класса с несколькими базовыми классами выглядит так:321322```python323class DerivedClassName(Base1, Base2, Base3):324 <statement-1>325 .326 .327 .328 <statement-N>329```330331Для классов старого стиля действует единственное правило – обход в глубину, слева направо. Таким образом, если атрибут не найден в `DerivedClassName`, он ищется в `Base1`, затем (рекурсивно) в базовых классах `Base1`, и только если его там нет, он ищется в `Base2` и так далее.332333(Некоторым людям обход в ширину – поиск в `Base2` и `Base3` перед базовыми классами `Base1` – кажется более естественным. Однако для этого потребовалось бы знать, определён ли конкретный атрибут `Base1` на самом деле в `Base1` или в одном из его базовых классов, прежде чем можно будет предвидеть последствия конфликта имён с атрибутом `Base2`. Правило обхода в глубину не делает различий между прямыми и унаследованными атрибутами `Base1`.)334335Для [классов нового стиля](https://python-all.ru/2.7/glossary.html#term-new-style-class) порядок разрешения методов изменяется динамически, чтобы поддерживать кооперативные вызовы [`super()`](https://python-all.ru/2.7/library/functions.html#super). Этот подход известен в некоторых других языках с множественным наследованием как call-next-method и является более мощным, чем вызов super, используемый в языках с одиночным наследованием.336337В классах нового стиля динамическое упорядочение необходимо, поскольку все случаи множественного наследования образуют одну или несколько ромбовидных связей (когда хотя бы один из родительских классов может быть достигнут несколькими путями из самого нижнего класса). Например, все классы нового стиля наследуют от [`object`](https://python-all.ru/2.7/library/functions.html#object), поэтому любой случай множественного наследования даёт более одного пути к [`object`](https://python-all.ru/2.7/library/functions.html#object). Чтобы базовые классы не обрабатывались более одного раза, динамический алгоритм линеаризует порядок поиска таким образом, что сохраняется заданный в каждом классе порядок слева направо, каждый родитель вызывается только один раз, и алгоритм монотонен (то есть класс можно наследовать, не влияя на порядок приоритета его родителей). В совокупности эти свойства позволяют создавать надёжные и расширяемые классы с множественным наследованием. Подробнее см. [https://www.python.org/download/releases/2.3/mro/](https://python-all.ru/2.7/tutorial/classes.html).338339## 9.6. Приватные переменные и локальные ссылки классов340341«Приватных» переменных экземпляра, к которым нельзя обратиться иначе как изнутри объекта, в Python не существует. Однако существует соглашение, которому следует большая часть кода Python: имя с префиксом подчёркивания (например, `_spam`) следует рассматривать как непубличную часть API (будь то функция, метод или элемент данных). Его следует считать деталью реализации, которая может измениться без уведомления.342343Поскольку существует обоснованный вариант использования для закрытых членов класса (а именно, чтобы избежать коллизий имён с именами, определёнными в подклассах), для такого механизма предусмотрена ограниченная поддержка, называемая *искажением имён*. Любой идентификатор вида `__spam` (как минимум два начальных символа подчёркивания, не более одного конечного) текстуально заменяется на `_classname__spam`, где `classname` – это имя текущего класса с удалёнными начальными символами подчёркивания. Это искажение выполняется независимо от синтаксической позиции идентификатора, если он встречается в определении класса.344345Искажение имён полезно для того, чтобы подклассы могли переопределять методы, не нарушая внутриклассовых вызовов методов. Например:346347```python348class Mapping:349 def __init__(self, iterable):350 self.items_list = []351 self.__update(iterable)352353 def update(self, iterable):354 for item in iterable:355 self.items_list.append(item)356357 __update = update # приватная копия исходного метода update()358359class MappingSubclass(Mapping):360361 def update(self, keys, values):362 # предоставляет новую сигнатуру для update()363 # но не нарушает __init__()364 for item in zip(keys, values):365 self.items_list.append(item)366```367368Приведённый выше пример будет работать, даже если `MappingSubclass` введёт идентификатор `__update`, поскольку он заменяется на `_Mapping__update` в классе `Mapping` и на `_MappingSubclass__update` в классе `MappingSubclass` соответственно.369370Обратите внимание, что правила искажения в основном предназначены для предотвращения случайностей; по-прежнему можно получить доступ к переменной, считающейся закрытой, или изменить её. Это может быть даже полезно в особых обстоятельствах, например, в отладчике.371372Обратите внимание, что код, переданный в `exec`, `eval()` или `execfile()`, не считает имя класса вызывающего класса текущим классом; это похоже на действие оператора `global`, действие которого также ограничено кодом, скомпилированным в байт-код вместе. То же ограничение действует для `getattr()`, `setattr()` и `delattr()`, а также при прямом обращении к `__dict__`.373374## 9.7. Разное375376Иногда полезно иметь тип данных, аналогичный паскалевской «записи» (record) или сишной «структуре» (struct), объединяющий несколько именованных элементов данных. Пустое определение класса прекрасно подойдет:377378```python379class Employee:380 pass381382john = Employee() # Создать пустую запись сотрудника383384# Заполнить поля записи385john.name = 'John Doe'386john.dept = 'computer lab'387john.salary = 1000388```389390Фрагмент кода Python, который ожидает определённый абстрактный тип данных, часто может получить вместо него класс, эмулирующий методы этого типа данных. Например, если есть функция, форматирующая данные из файлового объекта, можно определить класс с методами `read()` и `readline()`, которые получают данные из строкового буфера, и передать его в качестве аргумента.391392У объектов методов экземпляра тоже есть атрибуты: `m.im_self` – это объект экземпляра с методом `m()`, а `m.im_func` – объект функции, соответствующий методу.393394## 9.8. Исключения – тоже классы395396Пользовательские исключения также определяются классами. С помощью этого механизма можно создавать расширяемые иерархии исключений.397398Существуют две новые (семантически) допустимые формы для оператора [`raise`](https://python-all.ru/2.7/reference/simple_stmts.html#raise):399400```python401raise Class, instance402403raise instance404```405406В первой форме `instance` должен быть экземпляром `Class` или класса, производного от него. Вторая форма является сокращением для:407408```python409raise instance.__class__, instance410```411412Класс в блоке [`except`](https://python-all.ru/2.7/reference/compound_stmts.html#except) совместим с исключением, если это тот же класс или его базовый класс (но не наоборот – блок except, содержащий производный класс, несовместим с базовым классом). Например, следующий код выведет B, C, D в таком порядке:413414```python415class B:416 pass417class C(B):418 pass419class D(C):420 pass421422for c in [B, C, D]:423 try:424 raise c()425 except D:426 print "D"427 except C:428 print "C"429 except B:430 print "B"431```432433Обратите внимание, что если бы блоки except были переставлены (с `except B` первым), то вывелось бы B, B, B – срабатывает первый подходящий блок except.434435При выводе сообщения об ошибке для необработанного исключения печатается имя класса исключения, затем двоеточие и пробел, и, наконец, экземпляр, преобразованный в строку с помощью встроенной функции [`str()`](https://python-all.ru/2.7/library/functions.html#str).436437## 9.9. Итераторы438439К настоящему моменту вы, вероятно, заметили, что большинство объектов-контейнеров можно перебирать с помощью оператора [`for`](https://python-all.ru/2.7/reference/compound_stmts.html#for):440441```python442for element in [1, 2, 3]:443 print element444for element in (1, 2, 3):445 print element446for key in {'one':1, 'two':2}:447 print key448for char in "123":449 print char450for line in open("myfile.txt"):451 print line,452```453454Такой стиль доступа ясен, краток и удобен. Использование итераторов пронизывает и объединяет Python. Под капотом оператор [`for`](https://python-all.ru/2.7/reference/compound_stmts.html#for) вызывает [`iter()`](https://python-all.ru/2.7/library/functions.html#iter) для объекта-контейнера. Функция возвращает объект-итератор, который определяет метод [`next()`](https://python-all.ru/2.7/library/stdtypes.html#iterator.next), получающий элементы контейнера по одному. Когда элементы заканчиваются, [`next()`](https://python-all.ru/2.7/library/stdtypes.html#iterator.next) возбуждает исключение [`StopIteration`](https://python-all.ru/2.7/library/exceptions.html#exceptions.StopIteration), которое сообщает циклу [`for`](https://python-all.ru/2.7/reference/compound_stmts.html#for) о завершении. В этом примере показано, как всё работает:455456```python457>>> s = 'abc'458>>> it = iter(s)459>>> it460<iterator object at 0x00A1DB50>461>>> it.next()462'a'463>>> it.next()464'b'465>>> it.next()466'c'467>>> it.next()468Traceback (most recent call last):469 File "<stdin>", line 1, in <module>470 it.next()471StopIteration472```473474Увидев механику протокола итератора, легко добавить поведение итератора в свои классы. Определите метод [`__iter__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__iter__), который возвращает объект с методом [`next()`](https://python-all.ru/2.7/library/stdtypes.html#iterator.next). Если класс определяет [`next()`](https://python-all.ru/2.7/library/stdtypes.html#iterator.next), то [`__iter__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__iter__) может просто вернуть `self`:475476```python477class Reverse:478 """Итератор для перебора последовательности в обратном порядке."""479 def __init__(self, data):480 self.data = data481 self.index = len(data)482483 def __iter__(self):484 return self485486 def next(self):487 if self.index == 0:488 raise StopIteration489 self.index = self.index - 1490 return self.data[self.index]491```492493```python494>>> rev = Reverse('spam')495>>> iter(rev)496<__main__.Reverse object at 0x00A1DB50>497>>> for char in rev:498... print char499...500m501a502p503s504```505506## 9.10. Генераторы507508[Генераторы](https://python-all.ru/2.7/glossary.html#term-generator) – это простой и мощный инструмент для создания итераторов. Они пишутся как обычные функции, но используют оператор [`yield`](https://python-all.ru/2.7/reference/simple_stmts.html#yield) всякий раз, когда нужно вернуть данные. Каждый раз, когда на нём вызывается [`next()`](https://python-all.ru/2.7/library/functions.html#next), генератор продолжает работу с того места, где остановился (он запоминает все значения данных и то, какой оператор был выполнен последним). Пример показывает, что генераторы можно создавать удивительно легко:509510```python511def reverse(data):512 for index in range(len(data)-1, -1, -1):513 yield data[index]514```515516```python517>>> for char in reverse('golf'):518... print char519...520f521l522o523g524```525526Всё, что можно сделать с генераторами, можно сделать и с итераторами на основе классов, как описано в предыдущем разделе. Что делает генераторы такими компактными – это то, что методы [`__iter__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__iter__) и [`next()`](https://python-all.ru/2.7/reference/expressions.html#generator.next) создаются автоматически.527528Ещё одна ключевая особенность: локальные переменные и состояние выполнения автоматически сохраняются между вызовами. Это делает функцию проще для написания и гораздо понятнее, чем подход с использованием переменных экземпляра, таких как `self.index` и `self.data`.529530В дополнение к автоматическому созданию методов и сохранению состояния программы, при завершении генераторы автоматически возбуждают [`StopIteration`](https://python-all.ru/2.7/library/exceptions.html#exceptions.StopIteration). В совокупности эти возможности позволяют легко создавать итераторы, не прилагая больше усилий, чем при написании обычной функции.531532## 9.11. Генераторные выражения533534Некоторые простые генераторы можно кратко записать в виде выражений, используя синтаксис, похожий на списочные включения, но с круглыми скобками вместо квадратных. Эти выражения предназначены для ситуаций, когда генератор сразу используется обрамляющей функцией. Генераторные выражения более компактны, но менее универсальны, чем полные определения генераторов, и, как правило, более дружественны к памяти, чем эквивалентные списочные включения.535536Примеры:537538```python539>>> sum(i*i for i in range(10)) # сумма квадратов540285541542>>> xvec = [10, 20, 30]543>>> yvec = [7, 5, 3]544>>> sum(x*y for x,y in zip(xvec, yvec)) # скалярное произведение545260546547>>> from math import pi, sin548>>> sine_table = dict((x, sin(x*pi/180)) for x in range(0, 91))549550>>> unique_words = set(word for line in page for word in line.split())551552>>> valedictorian = max((student.gpa, student.name) for student in graduates)553554>>> data = 'golf'555>>> list(data[i] for i in range(len(data)-1,-1,-1))556['f', 'l', 'o', 'g']557```558559Сноски560561**[1](https://python-all.ru/2.7/tutorial/classes.html#id1)**562563За исключением одного. Объекты модулей имеют секретный атрибут только для чтения [`__dict__`](https://python-all.ru/2.7/library/stdtypes.html#object.__dict__), который возвращает словарь, используемый для реализации пространства имён модуля; имя [`__dict__`](https://python-all.ru/2.7/library/stdtypes.html#object.__dict__) является атрибутом, но не глобальным именем. Очевидно, что использование этого нарушает абстракцию реализации пространства имён, и должно быть ограничено такими вещами, как посмертные отладчики.564