Глава 6.7. Пакеты и модули6.7.1. Пакеты6.7.1.1. Понятие пакетаPERL поддерживает разбиение программ на пакеты, каждый из которых имеет собственное пространство имен. Пакет (package) это просто отдельный файл, содержащий подпрограммы, константы и переменные. Такой файл компилируется независимо от остальных пакетов и имеет собственное пространство имен. Обычно пакет имеет расширение .pl или .pm (Perl Module). Пакет должен содержать оператор package имя который задает название его пространства имен. Этот оператор действует до конца блока, eval или текущего файла. Для включения пакета в программу мы используем встроенные функции require() или use(). Такой оператор называется декларацией пакета и действует до конца блока, eval или текущего файла. Чаще всего декларации пакетов размещаются в начале PERL-программ и действуют во всем файле. Рассмотрим в качестве примера пакет Cwd, который входит в стандартный дистрибутив PERLа. Этот пакет содержит несколько функций,
позволяющих нам считывать и изменять текущий каталог операционной системы. В частности, он содержит функцию cwd(),
возвращающую текущий каталог, и функцию chdir(), изменяющую текущий каталог. Для того, чтобы пользоваться всеми функциями
пакета Cwd, мы должны включить в свою программу директиву use Cwd; $curdir = cwd; print "$curdir\n"; Эта программа выводит на экран имя текущего каталога. Директива use Cwd 'chdir'; # импорт chdir из пакета Cwd chdir "/temp"; # вызов функции chdir пакета Cwd Cwd::chdir "/temp"; # то же самое CORE::chdir "/temp"; # вызов встроенной функции chdir Этот пример демонстрирует применение квалификатора пакет::имя. По сути дела, такая конструкция указывает компилятору, какое пространство имен нужно использовать. PERL поддерживает два специальных имени пакетов:
PERL допускает использование составных имен пакетов вида имя1::имя2. Такая конструкция предназначена для удобства классификации; кроме того, она указывает компилятору, где искать данный пакет. Сложные имена пакетов соответствуют дереву подкаталогов в библиотечном каталоге PERLа. Например, дистрибутив содержит пакеты File::Compare, File::Copy, File::Find и т. п. Эти имена говорят компилятору, что соответствующие пакеты хранятся в файлах File/Compare.pm, File/Copy.pm и File/Find.pm библиотечного каталога соответственно (в системе Unix; в Windows, MacOS или VMS разделитель имени каталога и файла будет другим, но сути это не меняет). Главное, что при этом нужно понимать: составное имя пакета ничего не означает помимо сказанного. PERL не поддерживает относительных или вложенных пакетов, наследования и т. п. Каждый пакет, как бы мы его не назвали, является самостоятельной единицей компиляции, не связанной с другими пакетами. В частности, пакеты с именами OUTER::INNER и INNER не имеют ничего общего. Таблица символов пакета содержит только обычные его идентификаторы. Все специальные переменные и массивы всегда относятся к пространству имен main и не могут быть переопределены в пакетах. 6.7.1.2. Таблицы символовТаблица символов (т. е. список имен) пакета хранится в ассоциативном массиве с именем %имя::. Так, таблица символов главной программы содержится в ассоциативном массиве %main:: или, короче, %::, а таблица символов пакета File::Copy в ассоциативном массиве %File::Copy::. Элемент этого ассоциативного массива $package::{name} то же самое, что результат операции *name. Например, следующие операторы эквивалентны (но первый эффективнее, потому что проводит поиск по таблице на этапе компиляции): local *main::x = *main::y; local $main::{x} = $main::{y}; Мы можем, например, распечатать все переменные главной программы следующим оператором: print "$main::{$_}\n" foreach keys %main::; С элементами таблицы символов связаны еще несколько специальных конструкций, позволяющих получить о них дополнительную информацию. Все эти конструкции имеют вид *name{THING}, где THING ключевое слово, указывающее вид возвращаемой информации:
Пример: sub identify { my $glob = shift; print *{$glob}{PACKAGE}, '::', *{$glob}{NAME}, "\n"; } use Cwd; identify *STDIN; # выводит "main::STDIN" identify *Cwd::cwd; # выводит "Cwd::cwd" 6.7.1.3. Конструкторы и деструкторы пакетовПакет может содержать четыре специальных подпрограммы: BEGIN, CHECK, INIT и END. Перед именами этих подпрограмм ключевое слово sub необязательно. Подпрограмма BEGIN играет роль конструктора пакета. Она начинает выполняться, как только будет полностью загружена и скомпилирована, даже если остаток файла еще компилируется. Мы можем разместить в пакете несколько блоков BEGIN; они будут выполняться в порядке их размещения в файле. После исполнения этой подпрограммы она немедленно становится неопределенной и больше не может быть вызвана. Подпрограммы INIT аналогичны BEGIN, но исполняются перед запуском исполняющей системы PERLа. Они могут использоваться для инициализации пакетов. Подпрограмма END играет роль деструктора пакета. Она выполняется сразу после завершения программы (безразлично, нормального или по фатальной ошибке), но перед завершением работы интерпретатора. Мы можем разместить в пакете несколько блоков END; они будут выполняться в порядке, обратном их размещению в файле. Блоки END не выполняются, если perl запущен с опцией -c. Внутри подпрограммы END специальная переменная $? содержит код завершения программы, значение которой здесь можно изменить. Подпрограммы CHECK аналогичны END, но исполняются после завершения компиляции, но до начала исполнения программы. Они могут использоваться для проверки и сохранения компилированного кода программы. 6.7.1.4. Функции require, use и noВыше мы упомянули, что для включения пакета в программу используются встроенные функции require и use. Рассмотрим эти функции подробнее. Функция require имеет вид: require выражение Если выражение опущено, то берется значение переменной $_. Действия этой функции определяются семантикой выражения. Есди оно является числом или константой вида 'v5.6.1', то require проверяет, что текущая версия PERLа ($^V) не ниже заданной выражением. Если это не так, то работа программы завершается с соответствующей диагностикой. Примеры: require v5.6.0; # проверка версии PERL 5.6.0 require 5.6.0; # то же самое require 5.6; # эквивалентно require v5.600.0 require 5; # проверка версии PERL 5 В остальных случаях выражение задает имя пакета, который нужно включить в наш файл. В этом случае require выполняет следующие действия:
Иными словами, функция require находит заданный файл пакета и выполняет его. Пакет обязан вернуть истину, указывая, что его инициализация прошла успешно,
поэтому принято заканчивать любой пакет строкой require Some::Module будет искать в каталогах массива @INC файл Some/Module.pm, поскольку аргументом функции является слово. Но операторы $modname = 'Some::Module'; require $modname; или require "Some::Module"; будут искать (и, разумеется, не найдут) в каталогах массива @INC файл Some::Module, поскольку здесь аргументы функции не являются словами. В такой ситуации следует использовать конструкцию eval "require $modname"; Функция require выполняется на этапе исполнения программы. В большинстве случаях предпочтительнее производить поиск и загрузку пакетов на этапе компиляции, что обеспечивается функцией use, которая имеет следующие формы: use выражение use имя выражение список Первая форма проверяет текущую версию PERLа так же, как и функция require, но на этапе компиляции программы. Во второй форме имя задает имя пакета, выражение его версию, а список список имен, которые нужно из этого пакета импортировать. Имя должно быть словом, выражение и список не обязательны. Без выражения эта функция эквивалентна конструкции BEGIN { require имя; import имя список } Функция BEGIN обеспечивает выполнение указанных операторов на этапе компиляции; require загружает заданный модуль, а import вызывает загрузку из модуля перечисленных в списке имен. Реализация метода import определяется модулем, который мы загружаем. Вызов функции use с пустым списком use имя () эквивалентен конструкции BEGIN { require имя } Если выражение задано, то use вызывает метод VERSION класса имя, передавая ему выражение в качестве аргумента. Обычно метод VERSION сравнивает переданный ему аргумент с переменной $имя::VERSION и аварийно завершает работу программу, если затребованная версия больше, чем его. Противоположностью функции use является функция no, имеющая вид no имя список Эта функция вызывает метод В общем случае использование функции use предпочтительнее функции require, т. к. она проверяет наличие запрошенного модуля и импортируемых символов в нем при компиляции программы, а не посреди ее выполнения. Существует, однако, ситуация, в которой функция use не применима, а именно тот случай, когда два модуля обращаются к подпрограммам или переменным друг друга. В этой ситуации мы должны использовать функцию require. 6.7.1.5. Глобальные переменные: функция ourФункция our() аналогична функции my(), но создает не локальные, а глобальные переменные. Единственное ее применение состоит в том, что директива use strict vars позволяет обращаться к переменным, декларированным функцией our(), без указания имени пакета в пределах области действия этой функции. Синтаксис функции our() такой же, как у my(): our $var; our ($var1, $var2); 6.7.2. Модули6.7.2.1. Понятие модуляМодуль (module) это пакет, предназначенный для использования другими модулями или программами. Модули новое понятие для PERLа, которое появилось в его версии 5. Существует несколько механизмов импорта подпрограмм и переменных модуля (вспомните лозунг PERLа!), но мы сосредоточим свое внимание на объектно-ориентированной реализации модулей, поскольку она предоставляет программисту наиболее гибкие возможности и соответствует современным технологическим требованиям. Правила написания модулей мы подробно рассматриваем в следующей главе. Здесь же мы приводится краткий обзор библиотеки PERLа и самого важного в ней для создания собственных модулей модуля Exporter. 6.7.2.2. Библиотечные модулиВ состав дистрибутива PERLа входит обширная библиотека готовых модулей. В основном это файлы с расширением .pm, но некоторые могут иметь расширения .so (компилированные библиотечные файлы) или .al (автозагружаемые фрагменты модулей). Такие файлы создаются автоматически в процессе инсталяции модулей в систему. Старые библиотеки в формате PERL 4 могут иметь расширения .pl или .ph. Все библиотечные модули PERLа можно разбить на следующие категории:
CPAN (Comprehensive Perl Archive Network) является общедоступным источником модулей PERL. Это архив материалов, связанных с языком PERL, содержащий документацию, учебники, утилиты и исходные тексты более чем тысячи модулей PERLа. Для использования модулей CPAN мы должны найти нужный нам модуль, загрузить его и инсталировать в системе. Базовыми узлами CPAN являются узлы http://cpan.perl.com/ и ftp://ftp.perl.org/pub/CPAN/; в России узлы ftp://ftp.chg.ru/pub/lang/perl/CPAN/, ftp://cpan.rinet.ru/pub/mirror/CPAN/, ftp://ftp.aha.ru/pub/CPAN/ и ftp://ftp.sai.msu.su/pub/lang/perl/CPAN/. 6.7.2.3. Модуль ExporterОсобое место среди стандартных модулей занимает модуль Exporter, поскольку он реализует стандартный метод import, которым пользуется большинство других модулей. Напомним, что метод import неявно вызывается функцией use() при загрузке модуля в программу. Допустим, что мы пишем текст модуля Sample. Тогда для использования модуля Exporter мы должны начать его со строк package Sample; require Exporter; # загружаем модуль Exporter @ISA = qw(Exporter); # указываем, что неизвестные имена нужно искать в нем # и определяем: @EXPORT = qw(...); # символы, экспортируемые по умолчанию @EXPORT_OK = qw(...); # символы, экспортируемые по запросу %EXPORT_TAGS = (tag => [...]); # имена для наборов символов Массивы @EXPORT и @EXPORT_OK содержат списки символов, которые мы экспортируем во внешние пространства имен по умолчанию и по запросу соответственно. Экспортироваться могут переменные, подпрограммы или ссылки typeglob. Их имена обязаны содержать символ-префикс типа за исключением подпрограмм, для которых префикс & необязателен. Пример: @EXPORT_OK = qw(func %scalar @array %hash *typeglob); # func то же, что &func Ассоциативный массив %EXPORT_TAGS дает нам еще один механизм выборочного импорта символов. Он позволяет связать с группами символов любые символьные теги. Задавая эти теги, мы можем импортировать соответствующие группы символов. Все символы, перечисленные в группах %EXPORT_TAGS, должны присутствовать в массивах @EXPORT и/или @EXPORT_OK. Например, программы, которые обращаются к модулю Sample, должны содержать один из операторов use Sample; # импортировать символы, заданные по умолчанию use Sample qw(...); # импортировать символы, указанные в списке use Sample @{$EXPORT_TAGS{'tag'}}; # импортировать группу символов с тегом tag use Sample (); # не импортировать ничего Для удобства импорта введено следующее соглашение: если первое имя в списке импорта начинается с символа !, : или /, то этот список рассматривается как набор правил, управляющих списком импортируемых имен. Правила обрабатываются слева направо в соответствии со следующей таблицей:
Если список начинается с операции удаления, то считается, что ей предшествует операция :DEFAULT. Пусть наш модуль Sample содержит следующие операторы: @EXPORT = qw(A1 A2 A3 A4 A5); @EXPORT_OK = qw(B1 B2 B3 B4 B5); %EXPORT_TAGS = (T1 => [qw(A1 A2 B1 B2)], T2 => [qw(A1 A2 B3 B4)]); Тогда программа, обращающаяся к Sample, могла бы содержать оператор use Sample qw(:DEFAULT :T2 !B3 B5 /^.1/); который в данном случае эквивалентен оператору use Sample qw(A1 A2 A3 A4 A5 B4 B5 B1); Для отладки подобных конструкций удобно добавить в свою программу оператор Помимо описанных массивов модуль Exporter содержит несколько дополнительных возможностей, облегчающих управление экспортом символов. Поскольку все символы, перечисленные в %EXPORT_TAGS, обязаны присутствовать в @EXPORT или @EXPORT_OK, предусмотрены две функции export_tags и export_ok_tags, позволяющие легко добавлять символы в эти массивы. Пример: %EXPORT_TAGS = (XXX => [qw(aa bb cc)], YYY => [qw(cc dd ee)]); Exporter::export_tags('XXX'); # добавляет aa, bb и cc в @EXPORT Exporter::export_ok_tags('YYY'); # добавляет cc, dd и ee в @EXPORT_OK Функция use() при проверке версии модуля вызывает метод $module_name->require_version($value). В модуле Exporter этот метод реализован и проверяет переменную модуля $VERSION. При ее использовании нужно помнить, что версия рассматривается как плавающее число и потому 1.10 меньше, чем 1.9. Поэтому рекомендуется использовать номера версий с двумя цифрами после десятичной точки вида 1.09. Кроме того, Exporter содержит еще один метод, который используется в тех случаях, когда метод import модуля Exporter не может быть вызван напрямую. Этот метод имеет вид MyPackage->export_to_level($level, $package, @what); где $level целое число, задающее уровень экспорта, т. е. количество модулей в стеке вызовов, на которое нужно подняться, а @what массив экспортируемых символов, обычно @_. Параметр $package зарезервирован для будущих версий. Пусть, например, наш модуль Sample содержит строки @EXPORT_OK = qw($b); sub import { $Sample::b = 1; Sample->export_to_level(1, @_); } Тогда оператор Наконец, Exporter позволяет нам явно запретить экспорт каких-либо символов. Имена таких символов должны быть указаны в массиве @EXPORT_FAIL. При попытке импортировать эти символы Exporter вызывает оператор @failed_symbols = $module_name->export_fail(@failed_symbols); Если список, возвращаемый методом export_fail, пуст, то запрошенные символы экспортируются. В противном случае для каждого символа в этом списке генерируется соответствующее сообщение об ошибке. Мы можем переопределить в своем модуле метод export_fail или воспользоваться его реализацией по умолчанию, которая просто возвращает переданный ей список. | ||||||||||||||||||||||||||||||||||||