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" указатель

Эквивалент Питона для указателя С++ на экземпляр this является первым аргументом, добавляемым в вызовы функций методов (обычно принято называть его self). В вызове он обычно задается неявно, но явно используется в методах: нет скрытой области видимости экземпляра для неквалифицированных имен. Методы Питона суть просто функции, вложенные в оператор class, которые получают в качестве самого левого параметра предполагаемый объект экземпляра.

Виртуальные методы

В Питоне все методы и члены-данные являются виртуальными (virtual) в том смысле, как это понимается в C++: отсутствует понятие разрешения атрибутов на этапе компиляции исходя из типа объекта. Все квалификации атрибутов (object.name) разрешаются на этапе исполнения исходя из типа квалифицируемого объекта.

Чистые виртуальные методы

Методы, вызываемые суперклассом, но не определенные в нем, соответствуют понятию С++ о "чистых виртуальных" методах: методах, которые должны переопределяться в подклассах. Так как Питон не компилируется статически, для объявления такого случая не нужно специального синтаксиса, как в С++. Вызовы неопределенных методов возбуждают на этапе исполнения исключительную ситуацию ошибки имени, которую можно перехватывать в операторах try.

Статические члены

Не существует объявления static данных класса; вместо этого присваивания, вложенные в оператор class, генерируют имена атрибутов, связываемые с этим классом и совместно используемые всеми его экземплярами.

Закрытые (private) члены

Нет понятия о действительном ограничении доступа к атрибутам; все данные и методы суть public в смысле С++. Скрытие атрибутов регулируется соглашениями, а не синтаксисом: ограничения С++ public, private и protected не применимы (используются имена с подчеркиваниями в начале).

Интерфейсы const

Объекты могут быть немодифицируемыми, а имена — нет: эквивалент модификатора С++ 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".