Саммерфилд - Программирование на Python 3 (1077331), страница 66
Текст из файла (страница 66)
ча1пе. гпгват прес) Встроенная функция Гогват() — единственная действительно необходимая функция в объявлениях классов. Она принимает единственный объект и необязательную спецификацию формата и возвращает строку с объектом, отформатированным соответствующим образом. Когда объект используется в строке формата, вызывается метод Гсгват () объекта с самим объектом и спецификацией формата в виде аргументов. Метод возвращает строку с экземпляром, отформатированным соответствующим образом, как было показано ранее.
Все встроенные классы имеют соответствующие методы гогвзт (). В данном случае мы использовали метод (1оат. гсгват (), передавая значение с плавающей точкой и полученную строку формата. Того же эффекта можно было бы добиться другим способом: сег гсгват (зе1(, гпгват прес): гетпгп зе1г. ча1пе. гогват (гогват прес) При использовании встроенной функции Гогват() немного уменьшается объем ввода с клавиатуры, и программный код выглядит более очевидно. Никто не заставляет нас использовать функцию Го кват(), поэтому мы могли бы изобрести свой собственный язык форматирования и интерпретировать его внутри метода (о гват ( ); главное — чтобы он возвращал строку.
эзтат(светпсп Сет соп)плот[оп(*тпгт[ез): гетпгп Епхгузпо1(втп([Г1пат(х) Гог х тп Гпггтез))) Встроенная функция Втаттсветлсо() предназначена для использования в качестве декоратора, как видно из этого объявления. Статические методы — это обычные методы, которые не получают аргумент Ве1г или любой другой первый аргумент, который автоматически передавался бы интерпретатором РуФ)топ. Оператор й допускает объединение в цепочки так, чтобы, например, значения Г, с и и типа ЕсалуВоо1 могли быть объединены в одном выра- Собственные классы 299 женин т В В в Л.
Такой способ удобно использовать при небольшом числе объектов ЕоггуВоо1, но когда в выражении участвует десяток операндов или более, такой порядок вычислений становится слишком неэффективным, поскольку вызов функции производится для каждого оператора В. Благодаря методу, который определен здесь, мы можем получить тот же результат, используя единственный вызов функции ЕвгауВоо1.
ЕваауВоо1, сол)влс11ол(Е, В, Л). Этот вызов можно переписать более кратко, использовав экземпляр ЕшгуВоо1, но поскольку статические методы не получают аргумент ве1т, то при вызове метода относительно экземпляра и в выражении участвует сам экземпляр, мы должны передавать его явно, например, 1. сел) влст1оп(т, д, Л). Мы не показали соответствующую реализацию метода 0(в)злсшоп(), так как он отличается только именем и тем, что вместо функции е)л( ) использует функцию пах ( ) . Некоторые программисты считают использование статических методов несвойственным языку РуФ)топ и используют их только при переносе программ с других языков программирования (таких как С++ или )ача) или если метод не использует аргумент вв1т.
В языке Ру()топ вместо статических методов лучше создавать функции модуля, как будет показано в следующем подразделе, или методы класса, как будет показано в последнем разделе. Точно так же, когда переменная создается в пределах класса, но за пределами какого-либо метода, она становится статической переменной (переменной класса). В качестве констант обычно более удобно использовать частные глобальные переменные модуля, а переменные класса часто бывают полезны, когда необходимо хранить информацию, общую для всех экземпляров класса. Мы завершили реализацию класса ЕвггуВсо1 «с нуля».
Нам пришлось переопределить 15 методов (17, если бы мы выполнили минимум по всем четырем операторам сравнения) и реализовать два статических метода. В следующем подподразделе мы покажем альтернативную реализацию, на этот раз унаследовав класс (1оат. В этом случае нам придется переопределить всего восемь методов и реализовать две функции модуля, а также «исключить реализацию» 32 методов. В большинстве объектно-ориентированных языков программирования наследование используется, чтобы создать новый класс, обладающий всеми методами и атрибутами родительского класса, а также дополнительными методами и атрибутами.
Язык Ру$йоп целиком и полностью поддерживает эту парадигму, позволяя добавлять новые методы или переопределять унаследованные методы, чтобы изменить их поведение. Но, помимо этого, язык Ру$)топ позволяет исключать реализации методов, то есть определять новый класс так, как если бы он вообще не имел некоторых унаследованных методов.
Такой прием может вызвать протесты со стороны пуристов объектно-ориентированного программирования, так как он искажает идею полиморфизма, но ЗОО Глава б. Объектно-ориентированное программирование в языке РуФЬоп, по крайней мере иногда, этот прием может быть полезен. Создание типов данных из других типов данных Реализация класса ЕоггуВ001, которая обсуждается в этом подподразделе, находится в файле РиггуВооИИ.ру. Одно из основных отличий от предыдущей версии состоит в том, что статические методы ссп)опст!Оп() и О! в) опст!оп() в этой версии реализованы как функции модуля.
Например: пег соп)час!топ(*гоггтев): гетогп ЕоггуВоо1(нтп(тоггтев)) На этот раз программный код получился намного проще, чем прежде, потому что класс ЕоггуВ001А1!. ЕоггуВоо1 наследует класс (1сат, и потому объекты класса ЕоггуВ001 могут использоваться непосредственно, без необходимости выполнять какие-либо преобразования. (Дерево наследования приводится на рис. 6.5.) Порядок обращения к функции теперь также выглядит более понятным, чем прежде.
Вместо того чтобы указывать имя модуля и имя класса (или использовать экземпляр класса), после выполнения инструкции !арогт ЕоггуВ001А11 мы можем производить вызовы как ЕоггуВоо1А1!. соп) опст!оп(). Ниже приводится инструкция с1авв, объявляющая класс ЕоггуВ001, и реализация метода пви (): с1аев Еоггувоа1(Г1оат).
Сет пен (с1в, ча1ое=0.0): гетогп ворег(). пен,(сте, ча1ое тт 0.0 <= ча1ое <= 1.0 е1ее 0.0) Рис. 6.5. дерево наследования альт ернативнага класса риггуВва! Собственные классы При создании нового класса изменяемых объектов мы обычно полагаемся на метод оЬ)ест, пен (), который создает неинициализированную заготовку объекта. Но в случае создания классов неизменяемых объектов нам необходимо выполнить создание и инициализацию за один шаг, потому что неизменяемый объект не может изменяться после того, как будет создан. Метод пен,() вызывается еще до того, как объект будет создан (так, именно созданием объекта и занимается метод пен ()), поэтому метод не получает объект в виде аргумента зе1т, в конце концов, объект еще не существует.
В действительности метод пен () — это метод класса, он похож на обычный метод класса за исключением того, что он вызывается в контексте класса, а не в контексте экземпляра, и в первом аргументе ему передается класс. Имя аргумента с1з, в котором передается класс, определяется только соглашениями, так же как и имя ае1т используется для обозначения самого объекта исключительно в соответствии с общепринятыми соглашениями.
Итак, когда мы записываем инструкцию 1 = ВиггуВ001(0. 7), за кулисами интерпретатор вызывает метод ВиггуВ001. пен (ВиггуВ001, 0.7), чтобы создать новый объект — например, ~иггу, а затем метод ~иггу. (шт (), чтобы выполнить его инициализацию, и в результате возвращает ссылку на объект ~иггу — ту самую ссылку, которая будет записана в переменную т. Большая часть работы метода пен () выполняется реализацией базового класса оЬ)ест. пен (), а все, что делаем мы, — это гарантируем попадание значения в заданный диапазон. Методы класса определяются с помощью встроенной функции с1ааазетлос(), используемой как декоратор. Но нам совсем необязательно брать на себя труд писать В01ааазетлоа перед инструкцией Оет пен (), потому что интерпретатор уже знает, что этот метод всегда является методом класса.
Декоратор необходимо использовать, только когда создаются другие методы класса, как будет показано в последнем разделе главы. Теперь, когда мы познакомились с методами класса, можно познакомиться с другими разновидностями методов, существующими в языке РуФ)топ. Методы класса получают в первом аргументе, который передается интерпретатором Ру1)топ автоматически, свой класс. Обычные методы получают в первом аргументе, который передается интерпретатором Руа)топ автоматически„экземпляр класса, относительно которого был вызван метод.
Статические методы не имеют первого аргумента, добавляемого автоматически. Все перечисленные разновидности методов могут получать любое число аргументов (в виде второго и последующих аргументов — в случае методов класса и обычных методов, и в виде первого и последующих аргументов — в случае статических методов). аат гпиегт (ае1т): сетигп еиггуВоо1( х 0 - (1оат(аа1т)) Глава б. Объектно-ориентированное программирование Этот метод реализует поддержку битового оператора НЕ (-), как и прежде. Примечательно, что теперь вместо обращения к частному атрибуту, в котором хранилось значение Есгаубоо1, мы используем сам объект ве11. Это стало возможным благодаря наследованию класса (1оат, а это означает, что экземпляр ЕоггуВоо1 может использоваться везде, где ожидается объект типа Г1оат, естественно, за исключением методов, реализация которых была»исключена» из класса Еьгвубоо1.
Сет впп (ве1(, с!пег); гвтсгп еохвуВсс!(юп(ве1т, с!пег)) сег твпп (ве!т, с!пег): гетсгп есттуВсо!(в!п(ве!т, стьег)) Реализация логических операций также не изменилась (хотя программный код претерпел некоторые изменения). Здесь так же, как и в методе !пуегт,(), можно непосредственно использовать объекты ве1т и о!пег, как если бы они были объектами типа т1оат.
Мы опустили методы реализации операции ИЛИ, так как они отличаются только именами( ог () и !ог ()) и тем, что вместофункциив!и() используют функцию вах(). сег герг (ве!1); гетсгп ("(0)((1))".Гагват(ве1(. с1авв . паве всрег(). герг ())) Нам потребовалось переопределить реализацию метода герг (), потому что метод Г1оат, герг () просто возвращает число в виде строки, тогда как нам необходимо, чтобы было указано имя класса, чтобы репрезентативную форму представления можно было использовать в вызове функции еуа1().
Мы не можем просто передать методу в!г. Гогват() объект ве1( во втором аргументе, так как это приведет к бесконечной рекурсии метода герг (), поэтому мы вызываем реализацию базового класса. Нам не требуется переопределять метод в!г (), потому что вполне достаточно версии базового класса т1оа!. в!г (), которая и будет использоваться в отсутствие метода ЕьввуВоо1. в!г (). пег ьас1 (ве11): гетсгп ве!Г > 0.5 сег тпт (ве!г): ге!игл гсипп(ве!1) Когда объект типа Г1оат используется в логическом контексте, он будет рассматриваться как Еа1ве, когда его значение равно 0.0, и Тгве— в противном случае.