Документация Python неофициальный перевод

classes.md

526 строк · 61.3 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.3/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/3.3/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/3.3/library/functions.html#abs), и имена встроенных исключений); глобальные имена в модуле; а также локальные имена в вызове функции. В некотором смысле набор атрибутов объекта тоже образует пространство имён. Важно знать, что между именами из разных пространств имён нет абсолютно никакой связи; например, два разных модуля могут определить функцию `maximize` без путаницы – пользователям модулей нужно указывать впереди имя модуля.2627Между прочим, я использую слово *атрибут* для любого имени после точки – например, в выражении `z.real`, `real` является атрибутом объекта `z`. Строго говоря, ссылки на имена в модулях – это ссылки на атрибуты: в выражении `modname.funcname`, `modname` является объектом модуля, а `funcname` – его атрибут. В этом случае существует прямое отображение между атрибутами модуля и глобальными именами, определёнными в модуле: они используют одно и то же пространство имён! [\[1\]](https://python-all.ru/3.3/tutorial/classes.html#id2)2829Атрибуты могут быть только для чтения или для записи. В последнем случае возможно присваивание атрибутам. Атрибуты модуля доступны для записи: можно написать `modname.the_answer = 42`. Атрибуты, доступные для записи, также можно удалить с помощью оператора [`del`](https://python-all.ru/3.3/reference/simple_stmts.html#del). Например, `del modname.the_answer` удалит атрибут `the_answer` из объекта с именем `modname`.3031Пространства имён создаются в разные моменты и имеют разное время жизни. Пространство имён, содержащее встроенные имена, создаётся при запуске интерпретатора Python и никогда не удаляется. Глобальное пространство имён для модуля создаётся, когда читается определение модуля; обычно пространства имён модулей живут до завершения интерпретатора. Операторы, выполняемые на верхнем уровне интерпретатора (из скрипта или в интерактивном режиме), считаются частью модуля с именем [`__main__`](https://python-all.ru/3.3/library/__main__.html#module-__main__), поэтому у них есть собственное глобальное пространство имён. (На самом деле встроенные имена тоже живут в модуле; он называется [`builtins`](https://python-all.ru/3.3/library/builtins.html#module-builtins).)3233Локальное пространство имён для функции создаётся при её вызове и удаляется, когда функция завершается или возбуждает исключение, не обработанное внутри функции. (На самом деле, «забывание» было бы лучшим способом описать происходящее.) Конечно, рекурсивные вызовы имеют собственные локальные пространства имён.3435*Область видимости* – это текстовая область программы на Python, в которой пространство имён доступно напрямую. «Напрямую доступно» означает, что неквалифицированная ссылка на имя пытается найти это имя в пространстве имён.3637Хотя области видимости определяются статически, они используются динамически. В любой момент выполнения доступны как минимум три вложенные области видимости, чьи пространства имён непосредственно доступны:3839- самая внутренняя область, которая просматривается первой, содержит локальные имена40- области видимости любых внешних функций, которые ищутся начиная с ближайшей внешней области, содержат имена, не являющиеся локальными, но и не глобальными41- предпоследняя область содержит глобальные имена текущего модуля42- самая внешняя область (просматривается последней) – это пространство имён, содержащее встроенные имена4344Если имя объявлено как global, все ссылки и присваивания направляются прямо в среднюю область видимости, содержащую глобальные имена модуля. Для перепривязки переменных, найденных вне самой внутренней области видимости, можно использовать оператор [`nonlocal`](https://python-all.ru/3.3/reference/simple_stmts.html#nonlocal); если не объявлено nonlocal, такие переменные доступны только для чтения (попытка записи в такую переменную просто создаст *новую* локальную переменную во внутренней области видимости, оставив внешнюю переменную с тем же именем без изменений).4546Обычно локальная область видимости ссылается на локальные имена (текстуально) текущей функции. Вне функций локальная область видимости ссылается на то же пространство имён, что и глобальная: пространство имён модуля. Определения классов помещают ещё одно пространство имён в локальную область.4748Важно понимать, что области видимости определяются текстуально: глобальная область функции, определённой в модуле, – это пространство имён этого модуля, независимо от того, откуда и под каким псевдонимом функция вызывается. С другой стороны, фактический поиск имён выполняется динамически, во время выполнения – однако определение языка развивается в сторону статического разрешения имён, во время «компиляции», так что не полагайтесь на динамическое разрешение имён! (На самом деле, локальные переменные уже определяются статически.)4950Особая особенность Python: если нет действующего оператора [`global`](https://python-all.ru/3.3/reference/simple_stmts.html#global), присваивания имён всегда направляются в самую внутреннюю область видимости. Присваивания не копируют данные – они просто связывают имена с объектами. То же самое верно для удаления: оператор `del x` удаляет привязку `x` из пространства имён, на которое ссылается локальная область видимости. По сути, все операции, вводящие новые имена, используют локальную область видимости: в частности, операторы [`import`](https://python-all.ru/3.3/reference/simple_stmts.html#import) и определения функций связывают имя модуля или функции в локальной области видимости.5152Оператор [`global`](https://python-all.ru/3.3/reference/simple_stmts.html#global) используется, чтобы указать, что определённые переменные живут в глобальной области видимости и должны перепривязываться там; оператор [`nonlocal`](https://python-all.ru/3.3/reference/simple_stmts.html#nonlocal) указывает, что определённые переменные живут в объемлющей области видимости и должны перепривязываться там.5354### 9.2.1. Пример областей видимости и пространств имён5556Это пример, демонстрирующий, как ссылаться на разные области видимости и пространства имён, а также как [`global`](https://python-all.ru/3.3/reference/simple_stmts.html#global) и [`nonlocal`](https://python-all.ru/3.3/reference/simple_stmts.html#nonlocal) влияют на привязку переменных:5758```python59def scope_test():60    def do_local():61        spam = "local spam"62    def do_nonlocal():63        nonlocal spam64        spam = "nonlocal spam"65    def do_global():66        global spam67        spam = "global spam"68    spam = "test spam"69    do_local()70    print("After local assignment:", spam)71    do_nonlocal()72    print("After nonlocal assignment:", spam)73    do_global()74    print("After global assignment:", spam)7576scope_test()77print("In global scope:", spam)78```7980Вывод примера кода:8182```text83After local assignment: test spam84After nonlocal assignment: nonlocal spam85After global assignment: nonlocal spam86In global scope: global spam87```8889Обратите внимание, как *локальное* присваивание (по умолчанию) не изменило привязку *scope\_test* для *spam*. Присваивание [`nonlocal`](https://python-all.ru/3.3/reference/simple_stmts.html#nonlocal) изменило привязку *scope\_test* для *spam*, а присваивание [`global`](https://python-all.ru/3.3/reference/simple_stmts.html#global) изменило привязку на уровне модуля.9091Также можно заметить, что до присваивания [`global`](https://python-all.ru/3.3/reference/simple_stmts.html#global) для *spam* не было предыдущей привязки.9293## 9.3. Первое знакомство с классами9495Классы вводят немного нового синтаксиса, три новых типа объектов и некоторую новую семантику.9697### 9.3.1. Синтаксис определения класса9899Простейшая форма определения класса выглядит так:100101```python102class ClassName:103    <statement-1>104    .105    .106    .107    <statement-N>108```109110Определения классов, как и определения функций (операторы [`def`](https://python-all.ru/3.3/reference/compound_stmts.html#def)), должны быть выполнены, прежде чем они начнут действовать. (Можно, например, поместить определение класса в ветвь оператора [`if`](https://python-all.ru/3.3/reference/compound_stmts.html#if) или внутрь функции.)111112На практике операторы внутри определения класса обычно являются определениями функций, но допускаются и другие операторы, и иногда они полезны – мы вернёмся к этому позже. Определения функций внутри класса обычно имеют особую форму списка аргументов, продиктованную соглашениями о вызове методов – и это тоже объясняется позже.113114При входе в определение класса создаётся новое пространство имён, которое используется как локальная область видимости – таким образом, все присваивания локальным переменным попадают в это новое пространство имён. В частности, определения функций связывают имя новой функции здесь.115116Когда определение класса завершается нормально (доходит до конца), создаётся *объект класса*. По сути, это обёртка вокруг содержимого пространства имён, созданного определением класса; мы узнаем больше об объектах классов в следующем разделе. Исходная локальная область видимости (та, что действовала до входа в определение класса) восстанавливается, и объект класса привязывается к имени класса, указанному в заголовке определения класса (`ClassName` в примере).117118### 9.3.2. Объекты классов119120Объекты классов поддерживают два вида операций: обращение к атрибутам и создание экземпляров.121122*Ссылки на атрибуты* используют стандартный синтаксис, применяемый для всех ссылок на атрибуты в Python: `obj.name`. Допустимые имена атрибутов – это все имена, которые были в пространстве имён класса на момент создания объекта класса. Итак, если определение класса выглядело так:123124```python125class MyClass:126    """Простой пример класса"""127    i = 12345128    def f(self):129        return 'hello world'130```131132тогда `MyClass.i` и `MyClass.f` являются допустимыми ссылками на атрибуты, возвращающими целое число и объект функции соответственно. Атрибутам класса также можно присваивать значения, так что можно изменить значение `MyClass.i` через присваивание. `__doc__` также является допустимым атрибутом, возвращающим строку документации, принадлежащую классу: `"A simple example class"`.133134*Создание экземпляра* класса использует синтаксис вызова функции. Просто представьте, что объект класса – это функция без параметров, возвращающая новый экземпляр класса. Например (предполагая приведённый выше класс):135136```python137x = MyClass()138```139140создаёт новый *экземпляр* класса и присваивает этот объект локальной переменной `x`.141142Операция создания экземпляра («вызов» объекта класса) создаёт пустой объект. Многие классы предпочитают создавать объекты с экземплярами, настроенными на определённое начальное состояние. Поэтому класс может определить специальный метод с именем [`__init__()`](https://python-all.ru/3.3/reference/datamodel.html#object.__init__), например так:143144```python145def __init__(self):146    self.data = []147```148149Когда класс определяет метод [`__init__()`](https://python-all.ru/3.3/reference/datamodel.html#object.__init__), создание экземпляра класса автоматически вызывает [`__init__()`](https://python-all.ru/3.3/reference/datamodel.html#object.__init__) для только что созданного экземпляра класса. Таким образом, в этом примере новый инициализированный экземпляр можно получить так:150151```python152x = MyClass()153```154155Конечно, метод [`__init__()`](https://python-all.ru/3.3/reference/datamodel.html#object.__init__) может иметь аргументы для большей гибкости. В этом случае аргументы, переданные оператору создания экземпляра класса, передаются [`__init__()`](https://python-all.ru/3.3/reference/datamodel.html#object.__init__). Например,156157```python158>>> class Complex:159...     def __init__(self, realpart, imagpart):160...         self.r = realpart161...         self.i = imagpart162...163>>> x = Complex(3.0, -4.5)164>>> x.r, x.i165(3.0, -4.5)166```167168### 9.3.3. Объекты экземпляров169170Что можно делать с объектами экземпляров? Единственные операции, поддерживаемые объектами экземпляров, – это ссылки на атрибуты. Существует два вида допустимых имён атрибутов: атрибуты-данные и методы.171172*Атрибуты данных* соответствуют «переменным экземпляра» в Smalltalk и «членам данных» в C++. Атрибуты данных не нужно объявлять; как и локальные переменные, они появляются в момент первого присваивания. Например, если `x` – это экземпляр `MyClass`, созданный выше, следующий фрагмент кода выведет значение `16`, не оставив следов:173174```python175x.counter = 1176while x.counter < 10:177    x.counter = x.counter * 2178print(x.counter)179del x.counter180```181182Другой вид ссылки на атрибут экземпляра – это *метод*. Метод – это функция, которая «принадлежит» объекту. (В Python термин «метод» не уникален для экземпляров классов: другие типы объектов также могут иметь методы. Например, объекты списка имеют методы append, insert, remove, sort и так далее. Однако в дальнейшем обсуждении мы будем использовать термин «метод» исключительно для обозначения методов экземпляров классов, если явно не указано иное.)183184Допустимые имена методов объекта-экземпляра зависят от его класса. По определению, все атрибуты класса, являющиеся объектами-функциями, определяют соответствующие методы его экземпляров. Так, в нашем примере `x.f` является допустимой ссылкой на метод, поскольку `MyClass.f` – это функция, а `x.i` – нет, так как `MyClass.i` не является функцией. Но `x.f` – это не то же самое, что `MyClass.f`; это *объект метода*, а не объект функции.185186### 9.3.4. Объекты методов187188Обычно метод вызывается сразу после его связывания:189190```python191x.f()192```193194В примере с `MyClass` это вернёт строку `'hello world'`. Однако не обязательно вызывать метод сразу: `x.f` – это объект метода, который можно сохранить и вызвать позже. Например:195196```python197xf = x.f198while True:199    print(xf())200```201202будет продолжать печатать `hello world` до скончания времён.203204Что именно происходит при вызове метода? Вы, вероятно, заметили, что `x.f()` был вызван без аргумента выше, хотя определение функции `f()` задавало аргумент. Что случилось с аргументом? Разумеется, Python вызывает исключение, когда функцию, требующую аргумент, вызывают без аргументов – даже если аргумент фактически не используется...205206На самом деле вы, возможно, догадались: особенность методов в том, что объект передаётся как первый аргумент функции. В нашем примере вызов `x.f()` в точности эквивалентен `MyClass.f(x)`. В общем, вызов метода со списком из *n* аргументов эквивалентен вызову соответствующей функции со списком аргументов, который создаётся вставкой объекта метода перед первым аргументом.207208Если всё ещё непонятно, как работают методы, взгляд на реализацию может прояснить ситуацию. Когда происходит обращение к атрибуту экземпляра, который не является атрибутом-данным, выполняется поиск в его классе. Если имя соответствует атрибуту класса, являющемуся объектом функции, то создаётся объект метода путём упаковки (указателей на) экземпляр и найденный объект функции вместе в абстрактный объект: это и есть объект метода. При вызове объекта метода со списком аргументов строится новый список аргументов из экземпляра и списка аргументов, после чего с этим новым списком вызывается объект функции аргументов.209210## 9.4. Разные замечания211212Атрибуты данных переопределяют одноимённые атрибуты методов; чтобы избежать случайных конфликтов имён, которые могут вызвать труднонаходимые ошибки в больших программах, разумно использовать какое-либо соглашение, минимизирующее вероятность конфликтов. Возможные соглашения включают написание имён методов с заглавной буквы, добавление префикса к именам атрибутов данных в виде короткой уникальной строки (возможно, просто подчёркивания) или использование глаголов для методов и существительных для атрибутов данных.213214Атрибуты данных могут использоваться как методами, так и обычными пользователями («клиентами») объекта. Иными словами, классы не пригодны для реализации чистых абстрактных типов данных. На самом деле в Python нет возможности принудительно скрыть данные – всё строится на соглашениях. (С другой стороны, реализация Python, написанная на C, может полностью скрыть детали реализации и контролировать доступ к объекту, если это необходимо; эта возможность может использоваться расширениями Python, написанными на C.)215216Клиенты должны осторожно обращаться с атрибутами данных – они могут нарушить инварианты, поддерживаемые методами, если будут записывать свои значения в эти атрибуты. Обратите внимание, что клиенты могут добавлять собственные атрибуты данных к объекту экземпляра, не влияя на корректность методов, при условии отсутствия конфликтов имён – и снова соглашение об именах может избавить от многих проблем.217218Не существует краткой формы для обращения к атрибутам данных (или другим методам!) из методов. Я нахожу, что это на самом деле повышает читаемость методов: при беглом просмотре метода невозможно перепутать локальные переменные и переменные экземпляра.219220Often, the first argument of a method is called `self`. This is nothing more than a convention: the name `self` has absolutely no special meaning to Python. Note, however, that by not following the convention your code may be less readable to other Python programmers, and it is also conceivable that a *class browser* program might be written that relies upon such a convention.221222Любой объект функции, являющийся атрибутом класса, определяет метод для экземпляров этого класса. Совершенно необязательно, чтобы определение функции было текстуально вложено в определение класса: можно также присвоить объект функции локальной переменной в классе. Пример:223224```python225# Функция, определённая вне класса226def f1(self, x, y):227    return min(x, x+y)228229class C:230    f = f1231    def g(self):232        return 'hello world'233    h = g234```235236Теперь `f`, `g` и `h` – все являются атрибутами класса `C`, которые ссылаются на объекты-функции, и следовательно, все они являются методами экземпляров `C``h` в точности эквивалентно `g`. Заметьте, что такая практика обычно только сбивает с толку читателя программы.237238Методы могут вызывать другие методы, используя атрибуты-методы аргумента `self`:239240```python241class Bag:242    def __init__(self):243        self.data = []244    def add(self, x):245        self.data.append(x)246    def addtwice(self, x):247        self.add(x)248        self.add(x)249```250251Методы могут ссылаться на глобальные имена так же, как обычные функции. Глобальная область видимости, связанная с методом, – это модуль, содержащий его определение. (Класс никогда не используется в качестве глобальной области видимости.) Хотя редко встречаются веские причины для использования глобальных данных в методе, существуют многие законные применения глобальной области видимости: например, функции и модули, импортированные в глобальную область видимости, могут использоваться методами, а также функции и классы, определённые в ней. Обычно класс, содержащий метод, сам определён в этой глобальной области видимости, и в следующем разделе мы найдём несколько хороших причин, по которым методу может понадобиться ссылаться на собственный класс.252253Каждое значение – это объект, и поэтому у него есть *класс* (также называемый его *типом*). Он хранится как `object.__class__`.254255## 9.5. Наследование256257Разумеется, языковая возможность не заслуживала бы названия «класс», если бы не поддерживала наследование. Синтаксис определения производного класса выглядит так:258259```python260class DerivedClassName(BaseClassName):261    <statement-1>262    .263    .264    .265    <statement-N>266```267268Имя `BaseClassName` должно быть определено в области видимости, содержащей определение производного класса. Вместо имени базового класса также допускаются другие произвольные выражения. Это может быть полезно, например, когда базовый класс определён в другом модуле:269270```python271class DerivedClassName(modname.BaseClassName):272```273274Выполнение определения производного класса происходит так же, как и для базового класса. При создании объекта класса базовый класс запоминается. Это используется для разрешения ссылок на атрибуты: если запрашиваемый атрибут не найден в классе, поиск продолжается в базовом классе. Это правило применяется рекурсивно, если сам базовый класс является производным от какого-либо другого класса.275276В создании экземпляров производных классов нет ничего особенного: `DerivedClassName()` создаёт новый экземпляр класса. Ссылки на методы разрешаются следующим образом: ищется соответствующий атрибут класса, при необходимости спускаясь по цепочке базовых классов, и ссылка на метод считается допустимой, если в результате находится объект функции.277278Производные классы могут переопределять методы своих базовых классов. Поскольку методы не имеют особых привилегий при вызове других методов того же объекта, метод базового класса, который вызывает другой метод, определённый в том же базовом классе, может в итоге вызвать метод производного класса, который его переопределяет. (Для программистов на C++: все методы в Python фактически `виртуальные`.)279280Метод, переопределённый в производном классе, может фактически расширять, а не просто заменять метод базового класса с тем же именем. Есть простой способ напрямую вызвать метод базового класса: можно просто вызвать `BaseClassName.methodname(self, arguments)`. Это иногда бывает полезно и для внешнего кода. (Обратите внимание, что это работает только в том случае, если базовый класс доступен как `BaseClassName` в глобальной области видимости.)281282В Python есть две встроенные функции, работающие с наследованием:283284- Используйте [`isinstance()`](https://python-all.ru/3.3/library/functions.html#isinstance) для проверки типа экземпляра: `isinstance(obj, int)` возвращает `True` только если `obj.__class__` является [`int`](https://python-all.ru/3.3/library/functions.html#int) или классом, производным от [`int`](https://python-all.ru/3.3/library/functions.html#int).285- Используйте [`issubclass()`](https://python-all.ru/3.3/library/functions.html#issubclass) для проверки наследования классов: `issubclass(bool, int)` возвращает `True`, поскольку [`bool`](https://python-all.ru/3.3/library/functions.html#bool) является подклассом [`int`](https://python-all.ru/3.3/library/functions.html#int). Однако `issubclass(float, int)` возвращает `False`, поскольку [`float`](https://python-all.ru/3.3/library/functions.html#float) не является подклассом [`int`](https://python-all.ru/3.3/library/functions.html#int).286287### 9.5.1. Множественное наследование288289Python также поддерживает форму множественного наследования. Определение класса с несколькими базовыми классами выглядит так:290291```python292class DerivedClassName(Base1, Base2, Base3):293    <statement-1>294    .295    .296    .297    <statement-N>298```299300В большинстве случаев, в простейших ситуациях, поиск атрибутов, унаследованных от родительского класса, можно представить как обход в глубину, слева направо, без повторного поиска в одном и том же классе при пересечении иерархии. Таким образом, если атрибут не найден в `DerivedClassName`, он ищется в `Base1`, затем (рекурсивно) в базовых классах `Base1`, и если он не был найден там, он ищется в `Base2`, и так далее.301302На самом деле всё немного сложнее: порядок разрешения методов динамически изменяется, чтобы поддерживать кооперативные вызовы [`super()`](https://python-all.ru/3.3/library/functions.html#super). Этот подход известен в некоторых других языках с множественным наследованием как call-next-method и является более мощным, чем вызов super, встречающийся в языках с одиночным наследованием.303304Динамическое упорядочивание необходимо, поскольку во всех случаях множественного наследования присутствуют ромбовидные отношения (где по крайней мере один из родительских классов может быть достигнут несколькими путями из самого нижнего класса). Например, все классы наследуют от [`object`](https://python-all.ru/3.3/library/functions.html#object), поэтому любой случай множественного наследования предоставляет более одного пути к [`object`](https://python-all.ru/3.3/library/functions.html#object). Чтобы базовые классы не были доступны более одного раза, динамический алгоритм линеаризует порядок поиска таким образом, чтобы сохранялся порядок слева направо, указанный в каждом классе, каждый родитель вызывался только один раз, и это монотонно (то есть класс может быть подклассом без изменения порядка приоритета его родителей). В совокупности эти свойства позволяют проектировать надежные и расширяемые классы с множественным наследованием. Подробнее см. [http://www.python.org/download/releases/2.3/mro/](https://python-all.ru/3.3/tutorial/classes.html).305306## 9.6. Приватные переменные307308«Приватных» переменных экземпляра, к которым нельзя получить доступ извне объекта, в Python не существует. Однако существует соглашение, которому следует большая часть кода Python: имя с префиксом подчеркивания (например, `_spam`) следует рассматривать как непубличную часть API (будь то функция, метод или атрибут данных). Его следует считать деталью реализации, которая может изменяться без уведомления.309310Поскольку существует обоснованный сценарий использования для закрытых членов класса (а именно, избежание конфликтов имен с именами, определенными в подклассах), существует ограниченная поддержка такого механизма, называемого *name mangling*. Любой идентификатор вида `__spam` (не менее двух подчеркиваний в начале, не более одного в конце) текстуально заменяется на `_classname__spam`, где `classname` – это имя текущего класса с удаленными начальными подчеркиваниями. Это искажение выполняется независимо от синтаксической позиции идентификатора, если он встречается в определении класса.311312Искажение имён полезно для того, чтобы подклассы могли переопределять методы, не нарушая внутриклассовых вызовов методов. Например:313314```python315class Mapping:316    def __init__(self, iterable):317        self.items_list = []318        self.__update(iterable)319320    def update(self, iterable):321        for item in iterable:322            self.items_list.append(item)323324    __update = update   # приватная копия исходного метода update()325326class MappingSubclass(Mapping):327328    def update(self, keys, values):329        # предоставляет новую сигнатуру для update()330        # но не нарушает __init__()331        for item in zip(keys, values):332            self.items_list.append(item)333```334335Обратите внимание, что правила искажения в основном предназначены для предотвращения случайностей; по-прежнему можно получить доступ к переменной, считающейся закрытой, или изменить её. Это может быть даже полезно в особых обстоятельствах, например, в отладчике.336337Обратите внимание, что код, переданный в `exec()` или `eval()`, не считает имя класса вызывающего класса текущим классом; это похоже на эффект оператора `global`, действие которого также ограничено кодом, скомпилированным в байт-код вместе. То же ограничение применяется к `getattr()`, `setattr()` и `delattr()`, а также при прямом обращении к `__dict__`.338339## 9.7. Прочее340341Иногда полезно иметь тип данных, аналогичный паскалевской «записи» (record) или сишной «структуре» (struct), объединяющий несколько именованных элементов данных. Пустое определение класса прекрасно подойдет:342343```python344class Employee:345    pass346347john = Employee() # Создать пустую запись сотрудника348349# Заполнить поля записи350john.name = 'John Doe'351john.dept = 'computer lab'352john.salary = 1000353```354355Фрагмент кода Python, который ожидает конкретный абстрактный тип данных, часто может получить вместо него класс, эмулирующий методы этого типа данных. Например, если есть функция, которая форматирует некоторые данные из файлового объекта, можно определить класс с методами `read()` и `readline()`, которые получают данные из строкового буфера, и передать его в качестве аргумента.356357Объекты метода экземпляра также имеют атрибуты: `m.__self__` – это объект экземпляра, которому принадлежит метод `m()`, а `m.__func__` – это объект функции, соответствующий методу.358359## 9.8. Исключения – тоже классы360361Пользовательские исключения также определяются классами. С помощью этого механизма можно создавать расширяемые иерархии исключений.362363Существуют две новые допустимые (семантические) формы оператора [`raise`](https://python-all.ru/3.3/reference/simple_stmts.html#raise):364365```python366raise Class367368raise Instance369```370371В первой форме `Class` должен быть экземпляром [`type`](https://python-all.ru/3.3/library/functions.html#type) или класса, производного от него. Первая форма – это сокращение для:372373```python374raise Class()375```376377Класс в предложении [`except`](https://python-all.ru/3.3/reference/compound_stmts.html#except) совместим с исключением, если это тот же класс или его базовый класс (но не наоборот – предложение except, перечисляющее производный класс, несовместимо с базовым). Например, следующий код выведет B, C, D в таком порядке:378379```python380class B(Exception):381    pass382class C(B):383    pass384class D(C):385    pass386387for cls in [B, C, D]:388    try:389        raise cls()390    except D:391        print("D")392    except C:393        print("C")394    except B:395        print("B")396```397398Обратите внимание, что если бы предложения except были переставлены (сначала `except B`), то было бы выведено B, B, B – срабатывает первое подходящее предложение except.399400Когда выводится сообщение об ошибке для необработанного исключения, печатается имя класса исключения, затем двоеточие и пробел, и, наконец, экземпляр, преобразованный в строку с помощью встроенной функции [`str()`](https://python-all.ru/3.3/library/stdtypes.html#str).401402## 9.9. Итераторы403404К настоящему моменту, вероятно, уже заметно, что большинство объектов-контейнеров можно перебирать с помощью оператора [`for`](https://python-all.ru/3.3/reference/compound_stmts.html#for):405406```python407for element in [1, 2, 3]:408    print(element)409for element in (1, 2, 3):410    print(element)411for key in {'one':1, 'two':2}:412    print(key)413for char in "123":414    print(char)415for line in open("myfile.txt"):416    print(line)417```418419Такой стиль доступа ясен, лаконичен и удобен. Использование итераторов пронизывает и объединяет Python. За кулисами оператор [`for`](https://python-all.ru/3.3/reference/compound_stmts.html#for) вызывает [`iter()`](https://python-all.ru/3.3/library/functions.html#iter) на объекте-контейнере. Функция возвращает объект-итератор, который определяет метод [`__next__()`](https://python-all.ru/3.3/library/stdtypes.html#iterator.__next__), получающий доступ к элементам контейнера по одному. Когда элементов больше нет, [`__next__()`](https://python-all.ru/3.3/library/stdtypes.html#iterator.__next__) возбуждает исключение [`StopIteration`](https://python-all.ru/3.3/library/exceptions.html#StopIteration), которое сообщает циклу [`for`](https://python-all.ru/3.3/reference/compound_stmts.html#for) о завершении. Можно вызвать метод [`__next__()`](https://python-all.ru/3.3/library/stdtypes.html#iterator.__next__) с помощью встроенной функции [`next()`](https://python-all.ru/3.3/library/functions.html#next); этот пример показывает, как всё работает:420421```python422>>> s = 'abc'423>>> it = iter(s)424>>> it425<iterator object at 0x00A1DB50>426>>> next(it)427'a'428>>> next(it)429'b'430>>> next(it)431'c'432>>> next(it)433Traceback (most recent call last):434  File "<stdin>", line 1, in ?435    next(it)436StopIteration437```438439Разобравшись с механикой протокола итераторов, легко добавить итерационное поведение своим классам. Определите метод [`__iter__()`](https://python-all.ru/3.3/reference/datamodel.html#object.__iter__), который возвращает объект с методом [`__next__()`](https://python-all.ru/3.3/library/stdtypes.html#iterator.__next__). Если класс определяет `__next__()`, то [`__iter__()`](https://python-all.ru/3.3/reference/datamodel.html#object.__iter__) может просто вернуть `self`:440441```python442class Reverse:443    """Итератор для перебора последовательности в обратном порядке."""444    def __init__(self, data):445        self.data = data446        self.index = len(data)447    def __iter__(self):448        return self449    def __next__(self):450        if self.index == 0:451            raise StopIteration452        self.index = self.index - 1453        return self.data[self.index]454```455456```python457>>> rev = Reverse('spam')458>>> iter(rev)459<__main__.Reverse object at 0x00A1DB50>460>>> for char in rev:461...     print(char)462...463m464a465p466s467```468469## 9.10. Генераторы470471[*Генераторы*](https://python-all.ru/3.3/glossary.html#term-generator) – это простой и мощный инструмент для создания итераторов. Они пишутся как обычные функции, но используют оператор [`yield`](https://python-all.ru/3.3/reference/simple_stmts.html#yield), когда нужно вернуть данные. Каждый раз при вызове на нём [`next()`](https://python-all.ru/3.3/library/functions.html#next) генератор возобновляет работу с того места, где остановился (он запоминает все значения данных и то, какая инструкция была выполнена последней). Пример показывает, что генераторы могут быть тривиально простыми в создании:472473```python474def reverse(data):475    for index in range(len(data)-1, -1, -1):476        yield data[index]477```478479```python480>>> for char in reverse('golf'):481...     print(char)482...483f484l485o486g487```488489Всё, что можно сделать с помощью генераторов, можно также сделать с помощью итераторов на основе классов, как описано в предыдущем разделе. Генераторы такие компактные благодаря тому, что методы [`__iter__()`](https://python-all.ru/3.3/reference/datamodel.html#object.__iter__) и [`__next__()`](https://python-all.ru/3.3/reference/expressions.html#generator.__next__) создаются автоматически.490491Ещё одна ключевая особенность заключается в том, что локальные переменные и состояние выполнения автоматически сохраняются между вызовами. Это сделало функцию проще для написания и гораздо понятнее, чем подход с использованием переменных экземпляра, таких как `self.index` и `self.data`.492493В дополнение к автоматическому созданию методов и сохранению состояния программы, при завершении генераторы автоматически возбуждают [`StopIteration`](https://python-all.ru/3.3/library/exceptions.html#StopIteration). В совокупности эти функции позволяют легко создавать итераторы, не требуя больше усилий, чем написание обычной функции.494495## 9.11. Генераторные выражения496497Некоторые простые генераторы можно кратко записать в виде выражений, используя синтаксис похожий на списковые включения, но с круглыми скобками вместо квадратных. Такие выражения предназначены для ситуаций, когда генератор используется сразу же окружающей функцией. Генераторные выражения компактнее, но менее универсальны, чем полноценные определения генераторов, и обычно более экономичны по памяти, чем эквивалентные списковые включения.498499Примеры:500501```python502>>> sum(i*i for i in range(10))                 # сумма квадратов503285504505>>> xvec = [10, 20, 30]506>>> yvec = [7, 5, 3]507>>> sum(x*y for x,y in zip(xvec, yvec))         # скалярное произведение508260509510>>> from math import pi, sin511>>> sine_table = {x: sin(x*pi/180) for x in range(0, 91)}512513>>> unique_words = set(word  for line in page  for word in line.split())514515>>> valedictorian = max((student.gpa, student.name) for student in graduates)516517>>> data = 'golf'518>>> list(data[i] for i in range(len(data)-1, -1, -1))519['f', 'l', 'o', 'g']520```521522Сноски523524| [\[1\]](https://python-all.ru/3.3/tutorial/classes.html#id1) | За исключением одного. Объекты модулей имеют секретный атрибут только для чтения, называемый `__dict__`, который возвращает словарь, используемый для реализации пространства имён модуля; имя `__dict__` является атрибутом, а не глобальным именем. Очевидно, что использование этого нарушает абстракцию реализации пространства имён и должно быть ограничено такими вещами, как посмертные отладчики. |525| --- | --- |526