Лутц М. - Изучаем Python (1077325), страница 140
Текст из файла (страница 140)
Классы — это объекты: фабрики универсальных объектов зет гастпгу(ес1аяя, *агдя): № кертем переменного числа аргументов гетпгп арр1у(аС1аяя, егдя) № Вызывайте аС1аяя, или. аС1яяя7 агдя) с1еяя Брав. Сет Сост(яе17, веяяаде): рг1пт веяяеде с1аяя Регяеп еет тптт (яе17, паве. )оь): яе17 паве = паве яе17.)оЬ = )ое Фактически функция арр1у может вызывать любой вызываемый объект, включая функции, классы и методы. Функция тестогу в этом примере также может вызывать любые вызываемые объекты, а не только классы (несмотря на имя аргумента). Кроме того, обратите внимание: в последних версиях Рут)топ синтаксис вызова аС1еяя(*агдя) вообще предпочтительнее, чем вызов встроенной функции арр1у(аС1аяя, егдя).
Классы — это объекты, поэтому их легко можно передавать между компонентами программы, сохранять в структурах данных и т, д. Можно также передавать классы функциям, которые создают объекты произвольных типов, — в кругах, связанных с ООП, такие функции иногда называют фабриками. В языках со строгой типизацией, таких как С++, реализация таких функций — достаточно сложная задача, но в языке РуЬ)топ она становится почти тривиальной.
Функция арр1у и новейшая синтаксическая конструкция, с которыми мы познакомились в главе 17, могут вызывать любые классы с любым числом аргументов конструкторов за один присест, генерируя экземпляр любого типа 654 Глава 25. Шаблоны проектирования с классами оо)ес(1 = (аотогу(Враз) № Создать обьект Врал оо)ес(2 = (астогу(яегзоо, Со1ео', "доге") № Создат~ обьект Регзоо В этом фрагменте определена функция-генератор объектов с именем (асто гу.
Она ожидает получить объект класса (любого) вместе с одним или более аргументов конструктора класса. Она создает экземпляр с помощью функции арр1у и возвращает его. Остальная часть примера просто определяет два класса и генерирует экземпляры этих классов, передавая функции Гас(огу. И это единственная фабричная функция, которую вам придется написать на языке РуФЬоп, — она работает с любыми классами и с любыми аргументами конструктора. Следует заметить, что здесь возможно одно небольшое улучшение, которое заключается в обеспечении поддержки именованных аргументов конструктора; фабричная функция может собрать их в аргумент **агдз и передать функции арр!у в виде третьего аргумента: ое( гастогу(ас1азз. *агре, **кнагдз): № »дьагдз гетега арр1у(аС!азз, агдз, Кнагдз) № Вызеать аС1№зз К настоящему времени вы должны знать, что в языке РуФЬоп все является»объектом», включая и сами классы, которые в других языках, таких как С++, являются лишь объявлениями для компилятора.
Однако, как упоминалось в начале шестой части книги, в языке Ру1))оп только объекты, созданные из классов, являются субъектами ООП. Зачем нужны фабрики? Итак, чем же хороша функция Гассоту (помимо иллюстрации того, что классы являются объектами)7 К сожалению, довольно сложно продемонстрировать применение этого шаблона проектирования, потому что для этого необходимо привести фрагмент программного кода больший, чем позволяет пространство книги.
Тем не менее, такая фабрика могла бы помочь изолировать программный код от динамически настраиваемой конструкции объекта. Вспомним пример функции ргосеззог, представленный в главе 22, и затем пример применения принципа композиции в этой главе. В обоих случаях принимаются объекты, выполняющие чтение и запись обрабатываемого потока данных.
В оригинальной версии этого примера мы вручную передавали экземпляры специализированных классов, таких как Г11е)(г(сег и Яос(ге(- ()еабег, для адаптации под обрабатываемые потоки данных — позднее мы передавали жестко заданные объекты файла, потока и преобразования. В других случаях внешние источники данных могут определяться настройками в конфигурационных файлах или в элементах управления графического интерфейса. В таком динамическом мире не представляется возможным жестко задавать в сценарии объекты, реализующие интерфейс к потоку дан- Методы — зто объекты: связанные и несвязанные методы ных, но вполне возможно создавать их во время выполнения, в соответствии с содержимым конфигурационных файлов.
Например, в файле с настройками может определяться имя класса потока, который должен быть импортирован из модуля, и дополнительные аргументы конструктора. В этой ситуации могла бы пригодиться фабричная функция или эквивалентный ей фрагмент программного кода, потому что они могли бы позволить нам получить и передать классы, не определяя их заранее в программе. В действительности возможно представить себе, что требуемые классы даже не существовали в тот момент, когда мы писали свой программный код: с1аввпаве = ...определяется конфигурационнми файлом... с1аввагр = ...определяется конфигурационнми файлом ..
сарогс всгеаасурев я Специализироеанний программний код ас1авв = ресассг(всгеавсурев, с1аввпаве) з извлечь из модуля геаоег = гассогу(ас1авв, с1аввагр) я получить экземпляр ас!аввСс)аввагд) ргосеввог(геасег. ..,) Здесь встроенная функция оесассг снова используется для извлечения атрибута модуля, имя которого задано в виде строки (это все равно, что записать выражение ор).астг, где аССг — это строка), Так как этот фрагмент предполагает наличие у конструктора единственного аргумента, то, строго говоря, здесь не требуется ни функция гассогу, ни функция арр1у — мы могли бы просто создать экземпляр класса обращением ас1аввсс1аввагр).
Зги функции более полезны в случаях, когда количество аргументов неизвестно заранее, то есть когда универсальная фабричная функция способна повысить гибкость реализации. За дополнительной информацией по этой теме обращайтесь к книгам, которые посвящены вопросам ООП и шаблонам проектирования.
Методы — это объекты: связанные и несвязанные методы Методы — это разновидность объектов, напоминающая функции. Доступ к методам класса осуществляется через экземпляр класса или через сам класс и, следовательно, в языке РуСЬоп имеется две разновидности методов: Несвязаннсне методы класса: без аргумента веП Попытка обращения к функциональному атрибуту класса через имя класса возвращает объект несвязанного метода. Чтобы вызвать этот метод, необходимо явно передать ему объект экземпляра в виде первого аргумента. Связанные методы экземпляра: пара ве1г + функция Попытка обращения к функциональному атрибуту класса через имя экземпляра возвращает объект связанного метода. Интерпре- 656 Глава 25.
Шаблоны проектирования с классами татор автоматически упаковывает экземпляр с функцией в связанный объект метода, поэтому вам не требуется передавать экземпляр в вызов метода. Обе разновидности методов — это полноценные объекты. Они могут передаваться между программными компонентами, сохраняться в списках и т. д. При запуске оба требуют наличия экземпляра в первом аргументе (то есть значения для аргумента ве1(). Именно по этой причине в предыдущей главе было необходимо явно передавать экземпляр при вызове методов суперкласса из методов подкласса — с технической точки зрения такие вызовы порождают объекты несвязанных методов.
Вызывая объект связанного метода, интерпретатор автоматически подставляет экземпляр, который использовался при создании объекта связанного метода. Это означает, что объекты связанных методов обычно взаимозаменяемы с объектами простых функций и создание их особенно полезно в случае интерфейсов, изначально ориентированных на использование функций (реалистичный пример приводится во врезке «Придется держать в уме: связанные методы и функции обратного вызова«). Чтобы проиллюстрировать вышесказанное, предположим, что имеется следующее определение класса: с1евв Зрвз: оет оотт(ве1(, вевведе): ргтпт вевваде В обычной ситуации мы создаем экземпляр и сразу же вызываем его метод для вывода содержимого аргумента: оЬ)ест1 = Зрае() оь)ест1 оо(т('ьепо иогш') оь)естт = Яров() х = оЬ)ест1.ео!т х('Ье!1о иог1О') я Обьект связанного метода: экземпляр«функция я Го яе, ято м оо)еотт, боэ Г( '...
') С другой стороны, если для получения метода бом использовать имя класса, мы получим объект несвязанного метода, который просто ссы- лается на объект функции. Чтобы вызвать метод этого типа, необходи- мо явно передавать экземпляр класса в первом аргументе: оЬ)ес(1 = Ярая() т = Брам.оотт Я Обьект несвяэвнного метода т(оь)ест!, ьояоу') Ф рередвть экземпляр Однако в действительности попутно создается объект связанного метода — как раз перед круглыми скобками в вызове метода. Т.е.
мы можем получить связанный метод и без его вызова. Составное имя оь) ест. паве— это выражение, которое возвращает объект. В следующем примере это выражение возвращает объект связанного метода, в котором упакованы вместе экземпляр (оь]ес11) и метод (Брав. бо11). Мы можем присвоить этот связанный метод другому имени и затем использовать это имя для вызова, как простую функцию: 657 Еще раз о строках документирования Те же самые правила действуют внутри методов класса, когда использу- ются атрибуты аргумента зе1(, которые ссылаются на функции в клас- се. Выражение зе1т.
ве1воб возвращает объект связанного метода, пото- му что зе1( — это объект экземпляра: с1аве Еддв. Оет в((ееы, о): рыпт п Оет в2(ве1(): х = зе1(.в) х(42) № Еее один объект овяввнного иетодв № Выглядит квк обвиняя функыия Еддв().в2() № Выведет 42 Чаще всего вы будете вызывать методы немедленно, сразу же после указания составного имени, поэтому вы не всегда будете замечать, что попутно создается объект метода. Но как только вы начнете писать программный код, который вызывает объекты единообразным способом, то сразу обратите внимание на несвязанные методы, потому что обычно они требуют явной передачи экземпляра в первом аргументе.' Теперь, когда вы понимаете суть объектной модели методов, ознакомьтесь с примерами применения связанных методов во врезке «Придется держать в уме: связанные методы и функции обратного вызова» и еще раз прочитайте раздел предыдущей главы» са11 обрабатывает вызовы», где обсуждаются функции обратного вызова.