Python и C++
Из книги Марка Лутца "Прораммирование на Python". Приложение C
В этом приложении кратко суммированы некоторые различия между классами
Питона и C++. Систему классов Питона можно рассматривать как
подмножество системы классов С++. Сравнение с Modula3 могло быть более
тесным, но С++ сегодня является доминирующим языком ООП (под сегодня
следует понимать момент написания книги: конец 90х ... начало 2000х;
в настоящее время C++ вытесняется за счет Java и C#).
В Питоне модель классов намеренно упрощена — классы являются просто
объектами с присоединенными к ним атрибутами, которые могут быть связаны с
другими объектами классов. Поддерживается создание множественных экземпляров,
настройка (customization) путем наследования атрибутов и перегрузка
операторов, но объектная модель Питона сравнительно облегченная. Вот несколько
конкретных различий между Питоном и С++:
Реального различия между данными и методами в Питоне нет; те и другие
просто определяют именованные атрибуты экземпляров или классов,
привязанные к функциям или другим видам объектов. Атрибуты являются
именами, прикрепляемыми к объектам, доступ к которым происходит путем
квалификации: object.attribute. Методы являются просто атрибутами
класса, присваиваемыми функциям, обычно создаваемым вложенными
операторами def; члены являются просто именами атрибутов,
присваиваемыми объектам других видов.
Операторы классов создают объекты классов и присваивают их именам. Операторы,
выполняющие присваивание именам внутри оператора class, создают атрибуты
класса; классы наследуют атрибуты всех других классов, перечисленных в
заглавной строке их оператора class (поддерживается множественное
наследование; оно обсуждается ниже).
Вызов объекта класса, как если бы он являлся функцией, создает новый объект
экземпляра класса. Экземпляр возникает с пустым пространством имен,
наследующим имена из пространства имен класса; присваивание атрибутам
экземпляра (например, атрибутам self в функциях методов классов) создает
атрибуты в экземпляре.
Классы и экземпляры (и все содержащиеся в них данные) автоматически удаляются,
когда на них больше нет ссылок. Нет оператора new (вместо него вызываются
классы), а оператор Питона del лишь убирает одну ссылку, в отличие от
delete в С++.
Атрибуты классов и экземпляров, подобно простым переменным, начинают
существовать, когда осуществляется присваивание им, не объявляются заранее и
могут ссылаться на объект любого типа (они могут даже в разное время указывать
на объекты разных типов).
Наследование в Питоне обычно срабатывает при поиске значения по имени
атрибута: получив выражение вида object.attribute, Питон ищет в дереве
объектов пространства имен, начиная с object и выше, первое появление
имени attribute. Поиск согласно наследованию происходит также, когда к
объектам применяются операторы выражений и операции типов. Новый независимый
поиск соласно наследованию осуществляется для каждого вычисляемого выражения
object.attribute — даже выражения self.attr внутри метода функции
снова ведут поиск attr в объекте экземпляра, на который указывает
self, и выше.
Классы Питон на этапе исполнения являются объектами, находящимися в памяти,
их можно передавать в программе, обеспечивая своего рода источник информации о
типах времени выполнения (например, одна функция может генерировать
экземпляры произвольных классов, переданных в качестве аргумента). Объекты
классов и экземпляров несут информацию интерпретатора (например, словарь
атрибутов __dict__), а функция Питона type позволяет проверять тип
объекта. Атрибуты __class__ объектов экземпляров ссылаются на класс,
которым они созданы, а атрибуты __bases__ объектов классов дают
суперклассы класса (базовые классы).
Эквивалент Питона для указателя С++ на экземпляр this является первым
аргументом, добавляемым в вызовы функций методов (обычно принято называть его
self). В вызове он обычно задается неявно, но явно используется в методах:
нет скрытой области видимости экземпляра для неквалифицированных имен. Методы
Питона суть просто функции, вложенные в оператор class, которые получают в
качестве самого левого параметра предполагаемый объект экземпляра.
В Питоне все методы и члены-данные являются виртуальными (virtual) в том
смысле, как это понимается в C++: отсутствует понятие разрешения атрибутов на
этапе компиляции исходя из типа объекта. Все квалификации атрибутов
(object.name) разрешаются на этапе исполнения исходя из типа
квалифицируемого объекта.
Методы, вызываемые суперклассом, но не определенные в нем, соответствуют
понятию С++ о "чистых виртуальных" методах: методах, которые должны
переопределяться в подклассах. Так как Питон не компилируется статически, для
объявления такого случая не нужно специального синтаксиса, как в С++. Вызовы
неопределенных методов возбуждают на этапе исполнения исключительную ситуацию
ошибки имени, которую можно перехватывать в операторах try.
Не существует объявления static данных класса; вместо этого присваивания,
вложенные в оператор class, генерируют имена атрибутов, связываемые с этим
классом и совместно используемые всеми его экземплярами.
Нет понятия о действительном ограничении доступа к атрибутам; все данные и
методы суть public в смысле С++. Скрытие атрибутов регулируется
соглашениями, а не синтаксисом: ограничения С++ public, private и
protected не применимы (используются имена с подчеркиваниями в начале).
Объекты могут быть немодифицируемыми, а имена — нет: эквивалент модификатора
С++ const отсутствует. Ничто не мешает изменить имя или объект в методе, а
методы могут изменять модифицируемые аргументы (например, объект self).
Традиции и здравый смысл заменяют лишний синтаксис.
Нет прямого аналога параметрам-ссылкам С++. Методы Питона могут возвращать
несколько значений в виде кортежа и могут изменять передаваемые аргументы,
если они модифицируемы (например, путем присваивания атрибутам объекта или
изменения списков или словарей по месту). Но нет совмещения имен в вызове и
заголовке функции: аргументы передаются путем присваивания, благодаря чему
создаются совместно используемые ссылки на объекты.
Операторы перегружаются методами со специальными именами: синтаксиса, похожего
на operator+, нет, но эффект аналогичен. Например, атрибут класса
__add__ перегружает (перехватывает и выполняет) применение оператора +
к экземплярам класса; __getattr__ примерно похож на перегрузку -> в
С++. Произвольные выражения требуют кодирования правосторонних методов
(например, __radd__).
В Питоне осуществляется динамический контроль типов — имена служат ссылками на
произвольные объекты, и понятие объявления типа отсутствует. Шаблоны С++
неприменимы, и в них нет необходимости. Классы и функции Питона в целом могут
применяться к любому типу объектов, реализующему интерфейсные протоколы
(операции и операторы), предполагаемые кодом класса. Их объект не обязательно
должен иметь определенный тип данных.
В Питоне все является дружественным. Не понятия закрытости, поэтому любой
класс имеет доступ к внутренним элементам другого.
В Питоне полиморфизм основан на вызовах виртуальных методов: тип
квалифицирующего объекта определяет, что делают его методы. Так как в Питоне
типы аргументов не объявляются (контролируются динамически), нет ничего
похожего на перегрузку функций в С++, когда происходит обращение к различным
версиям функции в зависимости от типов данных ее аргументов. Можно явным
образом проверять типы и длину списка аргументов, а не писать отдельные
функции для каждой комбинации типов (см. встроенную функцию type и формат
аргументов функции *args).
Множественное наследование обозначается в коде перечислением более одного
суперкласса в круглых скобках за строкой заголовка оператора class. При
использовании множественного наследования Питон просто берет первое вхождение
атрибута при поиске в глубину слева направо в дереве суперклассов. Конфликты
множественного наследования Питон разрешает именно таким способом, а не
рассматривает их как ошибки.
Понятие С++ виртуальных базовых классов не вполне применимо в Питоне.
Экземпляр класса Питона является одним словарем пространства имен (с
указателем на класс для доступа к унаследованным атрибутам). Классы помещают
атрибуты в словарь экземпляра класса путем присваивания. Благодаря такой
структуре каждый атрибут существует лишь в одном месте — словаре экземпляра.
Для унаследованных атрибутов поиск в дереве суперклассов однозначно разрешает
ссылки.
Питон выполняет только один метод __init__, найденный в дереве
наследования объектов. Он не выполняет автоматически конструкторы всех
доступных классов; при необходимости конструкторы других классов вызываются
вручную. Но это не труднее, чем задавать аргументы конструкторов суперклассов
в С++. Деструкторы Питона (__del__) выполняются при сборке мусора
экземпляров (то есть удалении), а не в ответ на вызов delete.
Операторы С++ области видимости типа Superclass::Method используются для
расширения наследуемых методов и устранения неоднозначности в наследовании. В
Питоне ближайшим эквивалентом служит Superclass.Method, квалификация
объекта класса. Это не требуется для разрешения конфликтов наследования, но
может использоваться для отмены правила поиска по умолчанию и обращения к
суперклассам при расширении методов.
В Питоне ссылки на методы являются объектами и не используют специального
синтаксиса; их можно передавать, хранить в структурах данных и т.д. Объекты
методов бывают двух видов: связанные методы (когда экземпляр известен)
являются парами экземпляр-метод и вызываются впоследствии как простые функции;
несвязанные методы служат просто ссылками на объект метода функции и при
вызове требуют явного задания экземпляра.
Конечно, в Питоне есть дополнительные характеристики классов, отсутствующие в
С++, например, протоколы метаклассов: __setattr__ можно применять для
создания альтернативных интерфейсов, а указатель класса __class__ можно
переустановить, чтобы динамически поменять тип класса. Кроме того, атрибуты
класса можно произвольно менять на этапе исполнения: классы являются просто
объектами с присоединенными именами атрибутов.
Питон отличается от С++ во многих отношениях помимо моделей классов. Например,
в Питоне нет ни объявлений типов, ни этапов компиляции и компоновки; в Питоне
нельзя перегрузить =, как это можно сделать в С++ (присваивание не
является в Питоне оператором); указатели , одно из центральных понятий в
программировании на Си и С++, совершенно отсутствуют в Питоне (хотя ссылки на
объекты частично обладают их свойствами). Вместо указателей в Питон-программах
используются объекты первого класса (first-class objects), которые
автоматически размещаются и удаляются.
Большинство этих различий происходит из-за того, что Питон проектировался для
ускорения разработки, а не ускорения выполнения; значительная часть
дополнительного синтаксиса С++ помешала бы целям Питона. Полное введение в
классы и базовый язык Питон имеется в книге O'Reilly "Learning Python".