Саммерфилд - Программирование на Python 3 (1077331), страница 73
Текст из файла (страница 73)
Благодаря тому, что в языке РуСЬоп используется весьма эффективный алгоритм сортировки, который оптимизирован для работы с частично отсортированными списками, наша реализация едва ли даст заметный эффект, разве только при работе с огромными словарями. Тем 329 В заключение не менее она демонстрирует, что собственная реализация метода сору() в принципе может быть эффективнее, чем прием сору о! х = С]аввОГХ(х), который используется во встроенных типах языка РуФоп.
Точно так же, как и в случае с классом Вогсес[с в!, мы определили ссылку сору = сору, чтобы функция сору. сору() могла использовать нашу реализацию, а не свою собственную. Пе[ ча1ие аС(ве1Г, шзех): гегчгп ве1Г[ве1(. Хеув[спеех)] Еес веС ча!пе ас(ве1[, сапах, ча1ве); ве1([ве!г, хеув[!всех]] = ча1ве Эти два метода расширяют АР1 класса с!сс. Поскольку в отличие от обычного класса с!сС ключи в классе Яог(ес0!сС упорядочены, отсюда следует, что к объектам этого класса применимо понятие номера позиции. Например, первый элемент словаря имеет номер позиции О, а последний элемент — номер позиции 1еп(с) - 1.
Оба эти методаоперируют элементом словаря, ключ которого находится в позиции !пСсх внутри отсортированного списка ключей. Благодаря механизму наследования мы можем отыскивать значения в объекте Вогсес0!сс с помощью оператора доступа к элементу ([]), применяя его непосредственно к объекту ве1[, так как в этом отношении ве1г можно считать объектом класса с!сС. Если указанное значение индекса находится за пределами списка, методы возбуждают исключение 1пдехЕ г гог. На этом мы завершили реализацию класса Воггеср!сС.
Не так часто возникает необходимость создавать универсальные классы коллекций, подобные этому классу, но когда такая необходимость возникает, специальные методы позволяют полностью интегрировать наш класс в язык Ру(]сон, чтобы пользователи могли работать с ними, как с любыми другими встроенными классами или с классами из стандартной библиотеки. В заключение В этой главе были рассмотрены все фундаментальные основы поддержки объектно-ориентированного программирования в языке РуС)соп. Мы начали с демонстрации некоторых недостатков, присущих применению исключительно процедурного стиля программирования, и описали, как их можно избежать, используя объектно-ориентированный подход. Затем были описаны некоторые термины, используемые в объектно-ориентированном программировании, включая множество»совпадающих по значению» терминов, таких как базовый класс и суперкласс.
Мы увидели, как можно создавать простые классы с атрибутами данных и собственными методами. Кроме того, мы увидели, как наследовать классы и как добавлять дополнительные атрибуты данных и ме- 330 Глава 6. Объектно-ориентированное программирование тоды, а также, как «исключать» реализации нежелательных методов. Исключение методов необходимо при наследовании классов, когда возникает потребность ограничить круг методов, предоставляемых нашим подклассом; однако этот прием следует использовать с большой осторожностью, потому что он не соответствует ожиданиям, что подкласс может использоваться везде, где может использоваться базовый класс, то есть он нарушает принцип полиморфизма. Собственные классы могут быть интегрированы в язык РуФЬоп так, чтобы они поддерживали те же синтаксические конструкции, что и встроенные классы Ру1Ьоп или классы из стандартной библиотеки. Достигается это за счет реализации специальных методов.
Мы показали, как можно реализовать специальные методы поддержки операций сравнения, как обеспечить представление объектов в репрезентативной и строковой формах и как обеспечить преобразование в другие типы данных, такие как 1лт и Г1овт, когда это имеет смысл. Мы также показали, как реализовать метод пзвп (), чтобы экземпляры нестандартных классов могли использоваться в качестве ключей словаря или членов множества. Атрибуты данных сами по себе не обеспечивают механизм, гарантирующий установку корректных значений.
Мы увидели, насколько просто атрибуты данных замещаются свойствами, что позволяет создавать атрибуты, доступные только для чтения, а для свойств, доступных для записи, легко реализовать проверку корректности записываемых данных. В большинстве случаев классы, которые мы создаем, являются»неполными», потому что мы стремимся реализовать только те методы, которые действительно необходимы.
Это вполне оправданный прием, но у нас имеется возможность создавать полностью собственные реализации классов, которые предоставляют все соответствующие методы. Мы увидели, как создаются классы, хранящие одиночные значения, путем агрегирования других классов или более компактным способом — за счет использования наследования. Мы также увидели, как создаются классы, хранящие множество значений (коллекции). Нестандартные классы коллекций могут обеспечивать те же возможности, что и встроенные классы коллекций, включая поддержку оператора тл, функций 1еп(), .твг(), гемвгвев() и оператор доступа к элементам (()).
Мы узнали, что создание объекта и его инициализация — это разные операции и что язык Ру1Ьоп позволяет контролировать выполнение обеих операций, хотя практически всегда нам требуется предусматривать собственную реализацию только для операции инициализации. Мы также узнали, что можно безопасно возвращать объекты неизменяемых атрибутов данных, но в случае изменяемых атрибутов данных почти всегда желательно возвращать их копии, чтобы избежать непреднамеренного изменения объекта и приведения его в недопустимое состояние.
В заключение В языке Руз)зоп имеются обычные методы, статические методы, методы классов и функции модуля. Мы узнали, что в большинстве своем методы относятся к категории обычных методов. Иногда бывает полезно реализовать методы класса. Статические же методы используются редко, так как методы класса или функции модуля представляют собой более привлекательную альтернативу. Встроенная функция герг() вызывает специальный метод герг () объекта. Желательно, чтобы выполнялось еча1(герг(х)) == х, и мы увидели, как реализовать поддержку такой возможности. Когда отсутствует возможность создать строку репрезентативной формы, которую можно передавать функции еча1(), мы используем метод оо)еог.
герг () базового класса для воспроизведения репрезентативной формы в стандартном формате, несовместимом с функцией еча1(). Проверка типа объекта достаточно эффективно может быть выполнена с помощью встроенной функции за(пагапое(), хотя пуристы объектноориентированного стиля почти наверняка предпочли бы избегать ее использования. Доступ к методам базового класса выполняется посредством вызова встроенной функции аорег(), которая является основным способом избежать попадания в бесконечную рекурсию, когда возникает необходимость вызвать метод базового класса в переопределенной версии этого же метода в подклассе. Функции-генераторы и методы-генераторы обеспечивают средство выполнения отложенных вычислений. Они возвращают (посредством выражения у(е10) значения по одному за запрос и возбуждают исключение 31ор11егаг1оп, когда (если это происходит) заканчиваются возвращаемые значения.
Генераторы могут использоваться везде, где могут использоваться итераторы; и для конечных генераторов все их значения могут быть извлечены в кортеж или в список, если передать итератор, возвращаемый генератором, функции гор1е() или 1(а1(). Объектно-ориентированный подход практически всегда упрощает программный код в сравнении с исключительно процедурным подходом. Создавая свои классы, мы можем гарантировать доступность только допустимых операций (так как мы реализуем только соответствующие методы) и гарантировать, что ни одна из операций не переведет объект в недопустимое состояние (например, используя свойства для проверки присваиваемых значений).
Начав использовать объектно-ориентированный стиль, мы практически наверняка уйдем от использования глобальных структур данных и глобальных функций, оперирующих этими данными, создавая свои классы и реализуя методы, применяемые к ним. Объектно-ориентированный подход позволяет упаковывать в единую структуру данные и методы для работы с ними. Это помогает не запутаться во всех наших данных и функциях и упрощает создание программ, простых в сопровождении, так как функциональность хранится отдельно, в виде самостоятельных классов. Глава 6. Объектно-ориентированное программирование Упражнения а Ро!РС.
апа () Ф Рпспс. 1аеа () З Роспт. зиЬ () а Ро!пт. 1анЬ () П РР1пс. ач1 () З Ро!пс. 1вч1 () и РР1пс. Сгчеесч () З Ро1пС !!~чае!ч () з РЬ1пс.,г!Роге!ч () З РР1пс. СТ1ппгатч () Р=П«г Р «= П Р=П Р -= П Р=П р ° = п в=пуп р,г= и р = р /у п Р О= п Каждый из методов реализации комбинированных инструкций присваивания будет состоять всего нз четырех строк программного кода, а все остальные методы — из двух, включая строку с инструкцией г)ес, и, конечно же, все они очень просты и похожи между собой. С минимальным описанием и доктестом для каждого из них всего добавится порядка ста тридцати новых строк.