Глава 15. Создание пакетов и модулей
Коротко
Защищенность и Модульность — два великих принципа программирования. Perl обеспечивает их выполнение, предоставляя возможность разбивать программу на полуавтономные фрагменты так, что программисту не надо беспокоиться о конфликтах между ними и остальной частью программы. Для деления программы на независимые фрагменты используются пакеты Perl, которые создают непересекающиеся области имен (namespaces). Что такое область имен? Это часть программы со своей собственной областью видимости глобальных идентификаторов — другими словами, она функционирует как частная территория программиста.
На самом деле в Perl нет такой вещи, как «область видимости глобальных идентификаторов» — любая такая область ограничивается неким пакетом. Создавая пакет, вы получаете определенную гарантию того, что ваш код не смешается с переменными и подпрограммами другого фрагмента. Это позволяет организовывать код, предназначенный для многократного использования, в виде пакетов.
Кроме пакетов, существуют также модули Perl. Модули — это пакеты, организованные специальным образом. Их можно загружать их и интегрировать с конкретной программой. Наконец, в Perl существуют также классы — основа объектно-ориентированного программирования. О пакетах и модулях речь идет в этой главе, а о классах —в следующей.
Пакеты
Код, помещаемый в пакет, может размещаться в собственном файле, занимать несколько файлов, хотя несколько пакетов также могут делить один файл. Переключение между пакетами осуществляет команда package. В следующем примере создается пакет, сохраняемый как файл package1.pl:
package package1;
BEGIN { }
sub subroutine1 {print "Hello!\n";}
return 1;
END { }
Команда package начинает новый пакет package1. Обратите внимание на подпрограммы BEGIN и END. Первая (она обычно содержит инициализирующий код) выполяется сразу при загрузке пакета (точнее, она вызывается, как только интерпретатор доходит до нее, то есть еще до окончания загрузки пакета). Соответственно, подпрограмма END вызывается при завершении работы интерпретатора и может содержать код, выполняющий заключительные операции (например, закрывающий все открытые файлы). Подпрограммы BEGIN и END вызываются неявным образом (более того, BEGIN не удастся вызвать явно, даже если вы этого очень захотите: она уничтожается Perl сразу после использования). В силу особой роли, имена этих подпрограмм состоят из заглавных букв, и ключевое слово sub является для них необязательным.
Обратите внимание, что мы определили в пакете подпрограмму subroutine1. К ней можно будет обращаться в пределах кода, использующего пакет. Кроме того, стоит обратить внимание на команду return, расположенную вне какой-либо подпрограммы — она возвращает значение истина после загрузки пакета, показывая таким образом, что пакет готов к работе. (На самом деле возвращается последнее значение, вычисленное в теле модуля. Поэтому часто вместо return в последней строке пакета ставится просто цифра 1.)
Чтобы использовать в программе код пакета, надо поместить в сценарий команду require:
require 'package1.pl';
Теперь можно ссылаться на идентификаторы пакета package1, отделив его имя от идентификатора символами ::. В прежние времена в качестве разделителя выступал символ апострофа (одиночной кавычки), но теперь Perl следует в этом вопросе традиции, установленной другими языками программирования (а именно, С++):
require 'package1.pl';
Hello!
Можно поместить в пакет другие идентификаторы (например, переменные):
package package1;
BEGIN { }
sub subroutine1 {print "Hello!\n";}
return 1;
END { }
Чтобы сослаться на переменную вне пакета, придется сконструировать составное имя более сложным образом. Первым идет разыменовывающий префикс $, за ним — имя пакета, следом — разделитель ::, и только затем имя переменной, но уже без разыменовывающего префикса:
require 'package1.pl';
package1::subroutine1();
Hello!
1
Подсказка: Старый синтаксис, использующий апостроф вместо ::, по-прежнему поддерживается . Поэтому, например, если происходит интерполяция для строки "This is my $owner's house", Perl честно попытается найти переменную $s из пакета owner (по-видимому, имеется в виду нечто другое). Чтобы этого не произошло, воспользуйтесь фигурными скобками: "This is my ${owner}'s house".
Обратите внимание: таким образом нельзя добраться до переменных, описанных с ключевым словом my: они обладают лексической областью видимости и доступны только внутри модуля.
По умолчанию используется пакет main. Поэтому если вы не указываете имя пакета — как, например, для конструкции $::variable1, то Perl подставляет его сам (то есть, переменная $::variable1 эквивалентна $main::variable1).
На самом деле можно автоматически экспортировать имена, указанные в пакете (как, например, имя подпрограммы subroutine1) в текущую область имен. Это означает, что больше не нужно будет указывать имя пакета перед именем подпрограммы, когда понадобится ее вызвать. Чтобы добиться этого, необходимо использовать модули.
Модули
Модули — это пакеты, оформленные в виде отдельных файлов, причем имена последних совпадают с именами модулей и имеют расширение .pm. Соглашение Perl определяет, что имя модуля должно начинаться с заглавной буквы. Код, содержащийся в модуле, может экспортировать глобальные имена модуля в текущую область глобальных имен. Поэтому не нужно указывать имя пакета перед именем идентификатора при каждом обращении к нему.
Рассмотрим пример. Мы создаем модуль с именем Module1 и сохраняем его в файле Module1.pm. Код подпрограммы BEGIN, выполняемый в момент загрузки модуля, использует стандартный модуль Exporter, чтобы экспортировать в текущую область глобальных имен имя подпрограммы subroutine1:
package Module1;
BEGIN {
use Exporter ();
@ISA = 'Exporter';
@EXPORT = '&subroutine1';
}
sub subroutine1 {print "Hello!\n";}
return 1;
END { }
Теперь этот модуль можно добавить к основному коду. Обычно для этого используется команда use. В случае использования команды require подключение выполняется в момент выполнения сценария, однако, с помощью use пакет загружается на этапе компиляции. По чистой случайности файлы, загружаемые командой require, и файлы, загружаемые командой use, по умолчанию имеют одно и то же расширение (.pm). С помощью следующего набора команд мы добавляем к программе модуль Module1 и вызываем автоматически экспортированную подпрограмму subroutine1:
use Module1;
subroutine1();
Hello!
Нам еще много надо узнать про пакеты Perl. Пакеты можно вкладывать друг в друга, разрешать экспортировать определенные имена и не экспортировать их по умолчанию, и даже вызывать несуществующие подпрограммы (последнее можно сделать с помощью подпрограммы AUTOLOAD). Обо всем этом и еще об очень многом рассказывается в основной части главы.
Непосредственные решения
Как создать пакет
Для создания пакета используется команда package:
package имя
package
Эта команда открывает новую область глобальных имен, (то есть, область имен данного пакета). В одном файле можно объявлять несколько пакетов; можно разбивать пакет на несколько файлов, но, как правило, один пакет соответствует одному файлу. Можно также использовать вложенные пакеты (см. далее раздел «Создание вложенных модулей»), но они не наследуют область глобальных имен от прародителя. Поэтому при использовании имен, определенных в родительском пакете, придется указывать полное имя. Пример пакета:
package package1;
sub subroutine1 {print "Hello!\n";}
return 1;
Имя main соответствует области глобальных имен головного сценария. Его не следует использовать явно, и оно подставляется автоматически для составных имен с разделителем ::, когда имя пакета не указано. Если для команды package не задано имя, то это означает, что не создается никакой области имен (в том числе и области глобальных имен главного сценария) и, следовательно, придется указывать полное имя для всех последующих переменных и подпрограмм с помощью префикса в виде имени пакета. (Это даже более строгое требование, чем использование прагмы use strict, так как приходится указывать полные имена даже для подпрограмм.) С осторожностью надо использовать имена пакетов m, s, tr и y — конструкция вида m::... будет интерпретироваться Perl как операция поиска с пустым шаблоном (команда m/.../), а не как имя переменной из пакета m.
Рассмотрим пример:
package package1;
sub subroutine1 {print "Hello!\n";}
return 1;
После загрузки пакета последняя команда сообщает системе об успехе операции. Загрузка пакета выполняется с помощью команды require (она необходима, если пакет находится в файле, внешнем по отношению к данному пакету). По умолчанию команда require добавляет к имени пакета расширение .pm, так что пакет хранится в файле с другим расширением, необходимо указать его полное имя:
require 'package1.pl';
Команда require подключает код пакета к программе на этапе выполнения, а не компяции. После загрузки можно ссылаться на имена переменных и подпрограмм пакета указывая с помощью разделителя :: его имя перед идентификатором переменой:
require 'package1.pl';
package1::subroutine();
Hello!
Подсказка: В область имен пакета заносятся имена, начинающиеся с букв и символов подчеркивания (если отбросить разыменовывающий префикс.) Исключением является переменная $_, всегда находящаяся в области имен главного сценария. Это относится также ко всем специальные переменным Perl и именам STDIN, STDOUT, STDERR, ARGV, ARGVOUT, ENV, INC и SIG без префикса–имени пакета (даже если эти имена используются для целей, отличных от встроенных объектов Perl).
Нет необходимости хранить пакеты в отдельных файлах. Приведенные выше команды в можно объединить в один файл, поскольку переключение в новую область видимости происходит сразу, как только интерпретатор встречает команду package:
package1::subroutine();
package package1;
sub subroutine1 {print "Hello!\n";}
Hello!
Обратите внимание, что в данном случае нет необходимости ни загружать пакет командой require, ни возвращать значение истина в конце кода, поскольку он находится в том же самом файле.
Подсказка: Область глобальных имен пакета представляет собой хеш, имя которого совпадает с имем пакета, но в конце добавляется разделитель :: (в нашем случае %package1::). Соответственно, имена, используемые внутри пакета, но помещаемые в область глобальных имен главного сценария (см. предыдущую подсказку), находятся в хеше %main::.
Создание пакета: конструктор BEGIN
Для инициализации пакета обычно используется подпрограмма BEGIN в теле пакета. Используя терминологию объектно-ориентированного программирования подпрограмму BEGIN можно назвать конструктором пакета.
В следующем примере переменной $text присваивается значение "Hello!\n", которое впоследствии используется в подпрограмме subroutine1:
package package1;
sub subroutine1
{
print $text;
}
return 1;
(В силу важности конструкторов ключевое слово sub перед именем BEGIN необязательно.) Теперь, поскольку переменная $text инициализирована, мы можем спокойно ее использовать при вызове подпрограммы данного пакета:
require 'package1.pl';
Hello!
Подпрограмма BEGIN выполняется непосредственно в момент загрузки (компиляции) пакета еще до завершения синтаксического разбора его содержимого (то есть, как только Perl находит фигурную скобку, закрывающую тело подпрограммы) — поэтому, в частности, она не должна ссылаться на подпрограммы, определенные ниже. После выполнения BEGIN, интерпретатор немедленно удаляет ее из области видимости и тела пакета, так что вам никогда не удастся вызвать подпрограмму BEGIN самостоятельно (она всегда вызывается неявно самим Perl). Можно использовать несколько подпрограмм BEGIN в теле пакета — они будут последовательно выполняться и выбрасываться из области видимости в порядке следования и по мере загрузки пакета.
Поскольку BEGIN вызывается столь рано, это удачное место для описания прототипов подпрограмм (если вы используете прототипы — см. раздел «Использование прототипов» в гл. 7). В ней также удобно описывать статические переменные пакета, невидимые извне, но сохраняющие значение между вызовами подпрограмм пакета (см. раздел «Устойчивые (статические) переменные» в гл. 7). Наконец, прагмы Perl часто реализованы именно в виде подпрограмм BEGIN, поскольку они выполняются в первую очередь и, следовательно, могут влиять на работу компилятора.
Создание пакета: деструктор END
Подобно тому, как с помощью BEGIN инициализируется пакет, в подпрограмме END можно выполнить некоторые завершающие операции при окончании работы интерпретатора, — например, чтобы закрыть открытые файлы и освободить используемые системные ресурсы. (Однако не следует слишком полагаться на работу END, поскольку сценарий может завершиться аварийным образом еще до того, как дело дойдет до их вызова.) Подпрограмма END называется деструктором пакета<$FТо, что автор называет «деструктором», в обычной практике программирования называется «финализатором» — например, почувствуйте разницу между деструкторами в С++ и методами finalize в Java. — Примеч. ред.>. В следующем примере мы печатаем прощальное сообщение с помощью END:
package package1;
sub BEGIN
{
$text = "Hello!\n";
}
sub subroutine1
{
print $text;
}
return 1;
(В силу важности деструкторов ключевое слово sub перед именем END не является обязательным.) А вот результат работы:
require 'package1.pl';
package1::subroutine1();
Hello!
Thank you for using package!
Вы можете определить в одном пакете несколько подпрограмм END, — они будут вызываться в порядке, обратном порядку следования в теле пакета. (Подпрограммы END, соответствующие разным пакетам, будут вызываться в порядке, обратном порядку загрузки пакетов с помощью команд require и use.) Как и BEGIN, подпрограммы END не могут вызываться пользователем, а только самим Perl (они просто не включается в область глобальных имен пакета, так что при попытке их вызова вы получите сообщение об ошибке: «Undefined subroutine»). Во время выполнения END в специальную переменную $? записывается код выхода — поэтому, во-первых, его можно изменить, присвоив этой переменной новое значение, а во-вторых, следует соблюдать осторожность при использовании внутри END команд, изменяющих значение переменной $? (например, вызовов системных команд).
Как определить текущий пакет
Имя текущего пакета хранится во встроенном идентификаторе __PACKAGE__. В следует примере имя текущего пакета выводится из подпрограммы subroutine1, входящей в пакет package1:
package package1;
BEGIN {}
sub subroutine1
{
print __PACKAGE__;
}
return 1;
END {}
А вот результат вызова подпрограммы subroutine1:
require 'package1.pl';
package1::subroutine1();
package1
Как разбить пакет на несколько файлов
В одном файле может храниться несколько пакетов — для этого достаточно использовать команду package столько раз, сколько нам нужно. Но как продолжить пакет за границы файла? Это, как оказывается, тоже достаточно просто — достаточно определить в нескольких файлах пакеты с одним и тем же именем с помощью команды package.
Предположим, что в файле file1.pl находятся команды Perl, определяющие подпрограмму hello (обратите внимание, что имя пакета задано как package1):
BEGIN {}
return 1;
END {}
Теперь создадим второй файл file2.pl, в котором также определяется пакет package1 и подпрограмма hello2:
BEGIN {}
return 1;
END {}
Теперь загрузим командой require файлы file1.pl и file2.pl и используем подпрограммы hello и hello2, относящиеся к пакету package1, но расположенные в двух разных файлах:
require 'file1.pl';
require 'file2.pl';
package1::hello();
package1::hello2();
Hello!
Hello again!
Практичнее будет разбивать пакет на два файла незаметно для пользователя. Для этого в подпрограмму BEGIN надо поместить команду require. Тогда файл package1.pl будет содержать код
package package1;
sub hello {print "Hello!\n";}
return 1;
END {}
а файл package2.pl — код
BEGIN {}
sub hello2 {print "Hello again!\n";}
return 1;
END {}
Теперь загрузка пакета и вызов подпрограмм hello и hello2 выглядит так:
require 'package1.pl';
package1::hello();
package1::hello2();
Hello!
Hello again!
Обратите внимание на порядок, в котором выполняется вызов подпрограмм BEGIN и END в случае разбивки пакета:
Первый файл test.pl:
package test;
BEGIN
{
print "Begin/0\n";
require 'test2.pl';
print "Begin/1\n";
}
END
{
print "End/A\n";
}
package test;
BEGIN
{
print "Begin/2\n";
}
END
{
print "End/B\n";
}
Второй файл test2.pl:
package test;
BEGIN
{
print "Begin/3\n";
}
END
{
print "End/C\n";
}
package test;
BEGIN
{
print "Begin/4\n";
}
END
{
print "End/D\n";
}
Главный файл:
require 'test.pl';
print "This is the test.\n";
Результат работы:
Begin/0
Begin/3
Begin/4
Begin/1
Begin/2
This is the test.
END/B
END/A
END/D
END/C
Создание модулей
Модуль Perl — это пакет, который хранится в файле с тем же именем, что и у пакета, и с расширением .pm. (Использование такого расширения упрощает загрузку модулей с помощью команд require и use, поскольку они используют расширение .pm по умолчанию.) Например, вот как определить модуль Module1, хранящийся в файле Module1.pm:
package Module1;
BEGIN { }
sub subroutine1
{
print "Hello!\n";
}
return 1;
END { }
Модули могут экспортировать имена в область глобальных имен программы (или другого модуля, если последний загружает его), поэтому префикс в виде имени модуля и разделителя :: при вызове функций или переменных модулей можно опустить. (Однако если вам больше нравятся имена с префиксами, можете оставить их.) Более подробно о том, как научить Perl по умолчанию экспортировать определенные имена модуля в текущую область глобальных имен, рассказывается в следующем разделе.
Подсказка: Можно включить прямо в тело модуля документацию (POD = Plain Old Documentation), описывающую правила работы с модулем. Она игнорируется при загрузке модуля, ее всегда можете извлечь в виде ASCII-файла или во множестве других форматов (man, texinfo, HTML, FrameMaker MIF, ASDC-compliant PostScript, и т.д.) с помощью утилит, входящих в состав Perl. Примеры внедрения документации в код можно найти в самих модулях, либо во встроенной документации Более подробно об этом рассказывается в следующем разделе.Perl.
Документирование модулей
Как известно, хорошо написанная программа должна содержать текст на естественном языке, поясняющий ее работу, а не только операторы алгоритмического языка программирования. Одним из классических способов документирования программы являются комментарии. Однако, если вы захотите включить большой фрагмент текста, комментарии неудобны — придется помещать символ # в начале каждой строки.
Подсказка: Хотя до сих пор об этом и не упоминалось, но Perl может обрабатывать строки, начинающиеся со знака комментария в первой позиции, специальным образом. А именно, конструкции вида
# line номер "имя-файла"
рассматриваются как директивы компилятору, переопределяющие имя текущего файла (__FILE__) и номер строки (__LINE__), который присвоен следующей входной строчке. Пример:
% perl
# line 200 "Anonymous File"
print __LINE__, "\n";
die "test error";
200
test error at Anonymous File line 201
Конечно, если вы помещаете свой текст после завершающей Perl-программу конструкции __END__, то тогда нет необходимости оформлять его как комментарий — компилятор будет игнорировать все, что расположено после __END__. Но, во-первых, удобнее помещать документацию непосредственно рядом с поясняемым кодом, а во-вторых, такой способ может конфликтовать с другими средствами Perl — в частности, с возможностями, описанными далее в подразделе «Использование автозагрузки и самозагрузки подпрограмм». К счастью, Perl, как всегда, предоставляет более удобный способ решить поставленную задачу. А именно, когда вы создаете программу на Perl, вы можете включить прямо в тело программы документацию (POD = Plain Old Document), описывающую, как надо работать с вашей программой или модулем. Она будет игнорироваться при загрузке, но вы всегда можете извлечь ее в виде ASCII-файла или даже в виде размеченного HTML-файла с помощью утилит, входящих в состав Perl.
Начало документации обозначается символом равенства (=) с последующим ключевым словом, расположенными в первой позиции строки без предшествующих пробелов, конец — конструкцией =cut, также расположенной в самом начале строки. Внутри документации могут располагаться дополнительные ключевые слова, также выделяемые символом равенства и расположенные в начале строки, специальные команды разметки, описываемые ниже, и собственно текст документации. Компилятор Perl игнорирует весь текст от первого знака равенства и до заключительной конструкции =cut для всех фрагментов документации, встреченных в теле программы. Наоборот, специальные утилиты вырезают фрагменты документации, встроенные в Perl-программу, объединяя их в единый документ и форматируя в соответствии с ключевыми словами и специальными командами разметки, игнорируя операторы Perl. Пример:
=pod (Начало встроенной документации)
=item myTest
Функция I<myTest> предназначена для
проверки согласованности моих данных.
Она вызывается без параметров и
использует глобальные переменные.
=cut (Конец встроенной документации)
sub myTest () {
......
}
При работе со встроенной в программу документацией необходимо учитывать следующий факт: компилятор будет игнорировать POD-текст, как только он встретит POD-метку (то есть, произвольный идентификатор с предшествующим знаком равенства, расположеный в начальной позиции строки). Однако, POD-утилиты, извлекающие из программы встроенную документацию, организованы так, чтобы проверять наличие POD-меток только в начале абзацев (то есть, после пустой строки) — это облегчает синтаксический разбор входного текста. Тем самым, например, вы можете включать POD-комментарии, которые не видны ни компилятору, ни утилитам, работающим с документацией:
$a=3;
=secret unvisible text
Этот текст не видим ни компилятору,
ни стандартным POD-утилитам Perl!!!
=cut let us continue the program
$b=4;
Документация, включаемая в исходный текст программы Perl, структурируется в виде отдельных абзацев (как уже указывалось, это упрощает синтаксический разбор входного текста). Имеются три вида абзацев:
где абзац — это фрагмент текста, отделенный от остального текста пустой строкой в начале и в конце. (Обратите внимание, что некоторые POD-утилиты не рассматривают строку, содержащую пробелы, как пустую!)
Командный абзац начинается со знака равенства, за которым следует идентификатор команды. (Обратите внимание, что с точки зрения компилятора Perl командный абзац `=имя’, встреченный в теле программы, начинает фрагмент документации вплоть до заключительного `=cut’ независимо от того, является имя законной командой форматирования или же нет.) За идентификатором команды, отделенный от него пробелами, может идти текст, который либо является параметром команды, либо просто рассматривается как комментарий. В настоящее время допустимыми являются следующие команды:
Имеется ряд соглашений относительно списков. Так, команды `=item’ могут использоваться только внутри блока, ограниченного командами `=over’ и `=back’., а внутри каждого такого блока должна иметься хотя бы одна команда `=item’. Необходимо, чтобы команды `=item’ были согласованы между собой — так, если первая команда задает маркированный список, то и все остальные команды должны задаватьмаркированный список (аналогичное условие справедливо и для нумерованного или немаркированного списка). Наконец, ваша POD-утилита может иметь свои собственные правила обработки списков — так, некоторые утилиты запоминают тип списка по первой команде `=item’ и игнорируют соответствующие указания последующих команд `=item’.
=for html <br>
<p> Это просто абзац для HTML.</p>
=begin html
<br> Рисунок 1.1. <IMG SRC="figure.jpg"> <br>
=end html
Текстовый абзац начинается с первой позиции строки без начальных пробелов (и, естественно, не может начинаться со знака равенства). Такой абзац форматируется в соответствии со спецификой конкретной POD-утилиты (например, выравнивается по левому и правому краю) и с учетом команд форматирования, указанных в тексте. Допустимы следующие команды форматирования:
Кроме того, возможны следующие формы этой команды, когда в выходном документе вы увидите только указанный текст, а сама ссылка будет скрыта от ваших глаз:
(при этом текст не может содержать символов `|’ и `/’, а символы `<’ и `>’ внутри текста должны быть парными.)
Абзац типа verbatim начинается с пробела или знака табуляции. (Остальные строки абзаца могут начинаться с первой позиции.) Такой абзац воспроизводится в документе один к одному с единственным исключением — знаки табуляции выравнивают текст по границам столбцов, состоящих из 8-ми символов. Абзац не может содержать команды форматирования или escape-последовательности — любой текст выводится один к одному.
Как по умолчанию экспортировать имена модуля
С помощью модуля Exporter можно по умолчанию экспортировать имена модуля в область глобальных имен другого модуля или программы, (то есть той области, где производится загрузка модуля). При загрузку метод import модуля определяет, какие имена надо импортировать в область глобальных имен. (Метод — это подпрограмма объекта; о методах мы будем более подробно говорить в главе 16.) Стандартный модуль Exporter позволяет настроить этот метод для текущего модуля.
Рассмотрим пример. Создадим модуль Module1 и экспортируем с помощью модуля Exporter в область глобальных имен точки загрузки подпрограмму subroutine1:
package Module1;
BEGIN
{
}
sub subroutine1 {print "Hello!\n";}
return 1;
END { }
Массив @ISA указывает Perl проверить модуль Exporter на предмет методов, которые не могут быть найдены в текущем модуле — а именно, на предмет метода import. Массив @EXPORT перечисляет имена (наличие разыменовывающих префиксов обязательно), экспортируемые по умолчанию. Если вы хотите экспортировать несколько имен, нужно задать команду типа:
@EXPORT = qw(&subroutine1 &subroutine2 $variable1)
Теперь если какой-то код загрузит данный модуль, подпрограмма subroutine1 автоматически добавляется к области глобальных имен этого кода. Поэтому эту подпрограмму можно будет вызывать без префикса в виде имени модуля:
Hello!
Вы можете также указать Perl, какие имена разрешено экспортировать в текущую область глобальных имен, но не следует экспортировать по умолчанию. Следующий раздел рассматривает эту тему более подробно.
Как разрешить экспорт имени, но по умолчанию его не экспортировать
Хотя имена модуля можно экспортировать по умолчанию при загрузке, осторожность еще никому не мешала (в конце концов, пакеты и модули и создаются отчасти затем, чтобы избежать пересечения и наложения областей имен). Разрешается указывать Perl, какие имена можно экспортировать, но по умолчанию не делать этого. Для этого необходимо поместить такие имена в массив @EXPORT_OK и использовать модуль Exporter:
package Module1;
BEGIN
{
use Exporter();
@ISA = qw(Exporter);
}
sub subroutine1 {print "Hello!\n";}
return 1;
END { }
Теперь код, использующий данный модуль, способен импортировать подпрограмму subroutine1, но при этом по умолчанию имя этой подпрограммы не появится в глобальной области видимости программы, если только пользователь не потребует этого явно. Вот как выглядит импорт имени subroutine1 в другую глобальную область имен:
Hello!
Отметим, что при такой форме команды use импортируются только имена, перечисленные после имени модуля, даже если у модуля есть другие идентификаторы, экспортирующиеся по умолчанию. Чтобы перечисленные имена появились в глобальной области имет, они должны существовать и быть помечены как экспортируемые.
Как отключить импорт имен при загрузке модуля
Если вы не хотите, чтобы модуль экспортировал в область глобальных имен свои имена, описанные как экспортируемые по умолчанию, надо добавить пустой список (то есть, пару круглых скобок) после имени модуля, указанного в команде use. Например, если модуль Module1 по умолчанию экспортирует подпрограмму subroutine1, экспорт можно отключить:
subroutine1();
Undefined subroutine &main::subroutine1 called at
script.pl line 2
Как запретить экспорт имени
Если вы не хотите, модуль экспортировал определенные имена, их надо перечислить во время работы с модулем Exporter в массиве @EXPORT_FAIL. В качестве примера мы приведем модуль Uptime.pm, работающий как в операционных системах семейства Unix, так и в Windows. Этот модуль экспортирует подпрограмму uptime, показывающую, сколько времени компьютер находится включенным. Чтобы получить это значение, мы вызываем команду Unix uptime (заключив ее имя в обратные апострофы). Поскольку в Windows команды uptime не существует, то при использовании модуля для операционной системы Windows (мы проверяем этот факт, тестируя переменную $^O, содержащую имя операционной системы), подпрограмма uptime не должна экспортироваться:
package Uptime;
BEGIN
{
use Exporter;
@ISA = qw/Exporter/;
}
sub uptime {print `uptime`;}
return 1;
END { }
Пусть имеется сценарий script1.pl, в котором мы используем подпрограмму uptime из модуля Uptime:
use Uptime;
uptime();
В операционной системе семейства Unix на экране появится:
2:45pm 44 days, 20:32, 15 users, load average: 2.21, 1.48, 0.93
В случае Windows результат будет иным:
Sorry, no uptime available in Win32.
Undefined subroutine &main::uptime called at script1.pl line 2.
Подсказка: Относительно других возможностей модуля Exporter (например, об использовании хеша %EXPORT_TAGS) — см. документацию модуля и многочисленные примеры его использования в стандартных модулях Perl.
Экспортирование без помощи метода import
Когда кто-то использует написанный вами модуль, вызывается метод модуля import. Он импортирует имена, экспортируемые модулем. Некоторые модули имеют собственный метод import, что означает, что метод из модуля Exporter не вызывается. Однако можно организовать собственную процедуру импортирования, используя метод export_to_level модуля Exporter.
Обычно он вызывавется в пользовательских реализациях метода import. Например, вот так экспортируется переменная $variable1 модуля Module1 с помощью собственного метода import:
package Module1;
BEGIN { }
use Exporter();
@ISA = qw(Exporter);
@EXPORT = qw($variable1);
$variable1 = 100;
sub import
{
print "Im import\n";
}
return 1;
END { }
На самом деле в этом случает метод import делает не так уж много. Он выводит сообщение «Im import» и экспортирует переменную $variable1. Поскольку имя переменной экспортируется на один уровень вверх, (то есть к вызывающему модулю), методу export_to_level передается значение 1 и список экспортируемых имен. Теперь можно использовать модуль Module1 в другом пакете, а переменная $variable1 будет экспортироваться автоматически:
use Module1;
In import
$variable1 = 100
Создание вложенных модулей
В главе 13 при работе с Term::Cap мы видели, что модули могут быть вложенными. А именно, модуль Cap является подмодулем модуля Term. На самом деле с точки зрения физического расположения модули не вкладываются, (то есть, описание модуля Cap не является вложенным в описание модуля Term). Просто подмодуль размещается в каталоге с именем родительского модуля, расположенном внутри общего каталога библиотеки, — Perl рассматривает разделитель ::, как разделитель имен каталогов при поиске модулей, (то есть, модуль Module1::Code1 превращается в Module1/Code1 для Unix, и в Module1\Code1 для Windows).
Рассмотрим пример. Мы создадим модуль Module1::Code1 и используем его подпрограмму subroutine1. Чтобы написать исходный текст Module1::Code1, создадим новый каталог Module1, и поместим в него файл Code1.pm с исходным текстом. Убедитесь, что каталог входит в путь поиска модулей Perl, — например, создайте его, как подкаталог своего домашнего каталога или каталога библиотеки Perl, где хранится большинство стандартных модулей<$FК этому совету автора надо относится, скорее всего, как к неуместной шутке. Как правило, Perl при поиске модулей использует несколько стандартных каталогов — стандартная библиотека и дополнительные (нестандартные) модули находятся в разных каталогах, равно как в отдельных каталогах принято располагать платформозависимые модули (хотя при «ручной» сборке Perl это и можно изменить). Внедрение в эту структуру модулей пользователя на уровне подкаталогов сразу разрушит переносимость всей системы. Кроме того, создание подкаталога в каталоге стандартной библиотеки обычным пользователем возможно только в однопользовательской системе. — Примеч. ред.>:
package Module1::Code1;
BEGIN
{
use Exporter();
@ISA = qw(Exporter);
@EXPORT_OK = qw(&subroutine1);
}
sub subroutine1 {print "Hello!\n";}
return 1;
END { }
Теперь можно использовать подпрограмму subroutine1 из модуля Module1::Code1, которая всего-навсего выведет слово «Hello»:
use Module1::Code1;
subroutine1();
Hello!
Обратите внимание, что имя модуля, заданное в команде package, определено как Module1::Code1, а не как Code1. Это и есть истинное имя модуля, под которым он будет известен Perl: интерпретатор не занимается многоуровневым разбором имен типа Module1::Code1, когда он сперва находит имя Module1, а лишь затем — имя Code1. (То есть, вложенный каталог был нужен, чтобы Perl смог найти файл с текстом модуля при выполнении команды use, а имя пакета — чтобы ссылаться на него при доступе к подпрограммам и переменным пакета после загрузки.)
Проверка версии модуля
Теперь, когда вы научились создавать модули, другие программисты смогут использовать ваш код. Но что, если у них нет правильной версии вашего кода? Модуль Exporter поможет устроить проверку версии модуля. Для этого при работе надо определить переменную $VERSION и присвоить ей номер версии. Например, в следующем случае я задаю номер версии своего модуля 1.00:
package Module1;
BEGIN { }
use Exporter();
@ISA = qw/Exporter/;
@EXPORT = qw/$variable1/;
return 1;
END { }
Если теперь кем-либо используется модуль Module1, проверку версии можно выполнить с помощью метода require_version модуля Exporter. Если текущая версия не соответствует требуемой, генерируется сообщение об ошибке. Команда проверки выглядит следующим образом:
use Module1();
Module1 2 required – this is only version 1
(Module1.pm) at usem.pl line 2
Более короткий способ — указать проверку номера версии непосредственно в команде use, как это описано в разделе «Использование модулей Perl» главы 13. А именно, если сразу после имени модуля указать число, то Perl проверяет, чтобы номер версии модуля, указанный в переменной $VERSION, был не меньше номера, указанного в команде use (в противном случае генерируется сообщение об ошибке):
use Module1 2.0 ();
Module1 2 required – this is only version 1
at scipt1.pl line 1.
Автозагрузка модулей
Вызов несуществующе подпрограммы обычно приводит к сообщению об ошибке, — если только не была определена подпрограмма с именем AUTOLOAD. Именно она вызывается при обращении к несуществующей подпрограмме. Имя вызванной подпрограммы заносится в переменную $AUTOLOAD, а переданные ей аргументы — в массив @_.
Зачастую несуществующая подпрограмма на самом деле существует, но находится в модуле, который вы не хотите загружать, пока он не понадобится (именно поэтому данный процесс называется автозагрузкой). Однако подпрограммы может действительно не быть, и в этом случае AUTOLOAD должна имитировать ее работу. Например, можно научить AUTOLOAD выполнять системные команды, как подпрограммы, просто заключая вызванную подпрограмму и ее аргументы в обратные апострофы.
Рассмотрим пример. В модуле Autoload.pm мы используем AUTOLOAD для вывода имени и аргументов несуществующей подпрограммы. Обратите внимание, что подпрограмма AUTOLOAD экспортируется при загрузке модуля:
package Autoload;
BEGIN {
use Exporter ();
@ISA = qw(Exporter);
@EXPORT = qw(&AUTOLOAD);
}
sub AUTOLOAD () {
my $subroutine = $AUTOLOAD;
$subroutine =~ s/.*:://;
print "You called $subroutine with these arguments: ",
join(", ", @_);
}
return 1;
END { }
Заметьте, что имя подпрограммы, занесенное в переменную $AUTOLOAD, содержит имя пакета, в котором она (по мнению Perl) должна находиться. Например, если мы вызываем из сценария несуществующую подпрограмму printem, переменная $AUTOLOAD будет содержать текст «main::printem»:
use Autoload;
printem (1, 2, 3);
В модуле Autoload.pm имя модуля удаляется, а остается только имя самой подпрограммы. Вот результат вызова printem:
You called printem with these arguments: 1, 2, 3
Теперь, когда вы знаете, какая подпрограмма была вызвана, и с какими аргументами, с помощью команды require можнр загрузить содержащий ее модуль, или сымитировать ее поведение внутри AUTOLOAD.
Подсказка: Когда Perl не может найти подпрограмму в модуле, он в первую очередь ищет подпрограмму AUTOLOAD из этого модуля, и только в случае неудачи вызывает подпрограмму AUTOLOAD из текущей области видимости. Тем самым вы можете для каждого модуля определить собственную подпрограмму AUTOLOAD, обрабатывающую отсутствующие в данном модуле подпрограммы. Однако будьте осторожны при экспорте имен: в таком случае подпрограммы AUTOLOAD тоже должны экспортироваться, а подпрограммы из разных модулей могут перекрываться.
Использование автозагрузки и самозагрузки подпрограмм
Если вы не хотите загружать и компилировать весь сценарий, то можете разбить его на модули. Для этого служат модули AutoLoader и AutoSplit. В этом случае главная часть модуля организуется специальным образом, позволяющим во время работы сценария загружать и компилировать недостающие подпрограммы, а сами подпрограммы также располагаются в особо устроенных отдельных файлах. Как это сделать на практике, мы сейчас расскажем.
Самый простой (и стандартный) способ размещения подпрограммы по отдельным файлам так, чтобы их можно было динамически загружать с помощью средств модуля AutoLoader — использование модуля AutoSplit. Перед началом блока подпрограмм в исходном модуле помещается конструкция __END__ (в резудьтате компилятор будет игнорировать блок при загрузке модуля), и с помощью отдельного сценария с вызовом метода autosplit модуля AutoSplit модуль разбивается на фрагменты. Например, для DOS или Windows для этого можно создать .bat-файл pm2al.bat:
perl -w -e 'use AutoSplit; autosplit("%1", "%2")'
— здесь первым параметром выступает имя модуля, а вторым — каталог, в который будет записан результат. В Unix для sh-подобных оболочек это будет выглядеть так:
#! /bin/sh
perl -we 'use AutoSplit; autosplit($1, $2);’
Метод autosplit имеет следующий вид:
autosplit (файл, каталог, keep, check, modtime);
Первый параметр задает имя файла, который должен быть разбит на отдельные подпрограммы (при этом сам файл не меняется). В качестве второго параметра задается каталог, в котором будут размещаться отдельные файлы (по одному файлу на каждую подпрограмму). Если каталог не указано или в качестве него указана пустая строка, используется каталог lib/auto. Остальные параметры являются необязательными и представляют собой логические значения, управляющие работой метода autosplit.
Подсказка: Чтобы избежать возможных проблем, в одном файле должен находиться один модуль (пакет).
Метод autosplit разбивает подпрограммы модуля по отдельным файлам с расширением .al. Он также создает индекс autosplit.ix, расположенный в том же каталоге, что и файлы .al. (Этот файл будет использоваться для автозагрузки подпрограмм с помощью модуля AutoLoader. Модуль AutoLoader должен вызываться в заголовке модуля, расположенного до конструкции __END__ ; о нем пойдет речь ниже.) В качестве имени входного файла вы должны указать файл, в котором расположен исходный текст вашего модуля, и в котором блок подпрограмм отделен от заголовка конструкцией __END__. Если файл имеет стандартное расширение .pm, его можно не указывать — autosplit добавит расширение сам. Если файл расположен не в текущем каталоге, имя файла должно содержать полный путь. (Например, если модуль вложенный — то есть, он имеет имя вида имя1::имя2::имя3, — вы должны сами заменить разделитель :: имен пакетов на разделитель / имен каталогов.)
Имя каталога, заданное в качестве параметра autosplit, служит только начальной точкой отсчета для размещения файлов (как правило, это подкаталог auto каталога, в котором расположен исходный модуль). В качестве истинного места используется подкаталог указанного каталога, имя которого совпадает с именем модуля. Имя файла с расширением .al совпадает с именем подпрограммы, которая в него помещается. Например, если модуль называется Modul1, подпрограмма — subr1, а в качестве базового каталога указано "./auto", подпрограмма будет записана в файл ./auto/Modul1/subr1.al. Если же имя модуля — составное (то есть, имеет вид имя1::имя2::имя3), мы получим систему вложенных каталогов. Необходимо подчеркнуть, что имя входного файла используется только для доступа к файлу, — имя модуля метод autosplit извлекает из обрабатываемого им файла самостоятельно.
Подсказка: Некоторые операционные системы не поддерживают слишком длинных имен подпрограммы и/или пакета. В таких случаях AutoSplit усекает имя файла, о чем выдает предупреждающее сообщение, — может возникнуть конфликт имен файлов, соответствующих разным модулям и подпрограммам. Во избежание подобных ситуаций старайтесь при использовании AutoSplit и AutoLoader применять короткие имена.
Остальные параметры являются необязательными и представляют собой логические флаги, принимающие значения ноль или единица, которые управляют работой метода autosplit. Так, если третий параметр (keep) — это ложь, то все существующие файлы с расширением .al в каталоге, используемом методом autosplit для записи результата работы, удаляются, если для них в исходном модуле нет надлежащей подпрограммы (удаление старых версий). Если четвертый параметр (check) — это истина, то метод autosplit проверяет, что в заголовке модуля действительно используется модуль AutoLoader (и прекращает работу, если это не так). Наконец, если пятый параметр (modtime) — это истина, то метод autosplit проверяет, что исходный текст модуля не модифицировался со времени последнего вызова autosplit (сравнивая время создания индексного файла autosplit.ix и время последнего редактирования входного файла) и не выполняет никаких действий, если повторное разбиение модуля на подпрограммы не требуется. (Значения по умолчанию: $keep=0, $check=1, $modtime=1).
Подсказка: Если вы хотите, чтобы autosplit выводил на экран сообщения в процессе своей работы, перед его вызовом установите переменную $AutoSplit::VERBOSE в 1 или 2.
Чтобы модуль мог использовать механизм автозагрузки файлов, созданных с помощью autosplit, его главая часть должна быть организована специальным образом. Примером может служить модуль POSIX, который разбивает свои многочисленные подпрограммы на отдельные файлы и загружает их только тогда, когда они действительно требуются пользователю:
package POSIX;
use AutoLoader;
......
sub AUTOLOAD
{
$AutoLoader::AUTOLOAD = $AUTOLOAD;
goto &Autoloader::AUTOLOAD;
}
......
Здесь модуль POSIX определяет собственную подпрограмму AUTOLOAD, которая передает управление подпрограмме AUTOLOAD модуля AutoLoader — если подпрограмма, которую хочет вызвать пользователь, отсутствует, AUTOLOAD пытается найти и загрузить файл, в котором определен недостающий код. (На самом деле подпрограмма AUTOLOAD модуля POSIX устроена сложнее, но мы имеем дело с учебными примерами, а не реальными программами.)
Предположим, что модуль POSIX расположен в файле /usr/local/lib/perl5/POSIX.pm. Тогда AUTOLOAD попытается загрузить файл /usr/local/lib/perl5/auto/POSIX/подпрограмма.al. Если этот файл существует, AUTOLOAD обработает его содержимое и передаст управление подпрограмме вместе с полученными параметрами. После этого подпрограмма с указанным именем становится определенной и последующие вызовы проходят уже без участия AUTOLOAD. Ели используется многоуровневый пакет, — например, Term::Cap, расположенный в файле /usr/local/lib/perl5/Term/Cap.pm —AUTOLOAD будет учитывать многоуровневую структуру оглавлений (то есть, будет загружать файл /usr/local/lib/perl5/auto/Term/Cap/подпрограмма.al).
Подсказка: Если подпрограмма AUTOLOAD не может найти файл подпрограмма.al, для загрузки файла auto/подпрограмма.al используется команду require. Та ищет указанный файл по всем путям поиска (не обязательно связанных с текущим пакетом) и загружает первый найденный файл. В этом случае следует иметь в виду, что текущий подкаталог также входит в список путей поиска, так что файлы, полученные в результате действия метода autosplit, можно разместить как ./auto/подпрограмма.al. а, например, исходные модули, использованные для их генерации — как ./lib/модуль.pm. Однако если во время работы сценария с помощью функции chdir изменить текущий каталог, могут возникнуть проблемы.
Поскольку модуль POSIX устроен достаточно сложно, ему потребовалось определить собственную подпрограмму AUTOLOAD, которая, помимо вызова подпрограммы AUTOLOAD модуля AutoLoader, выполняет еще и некоторые дополнительные действия. В большинстве случаев достаточно просто импортировать в модуль подпрограмму AUTOLOAD модуля AutoLoader:
package MyModule;
use AutoLoader 'AUTOLOAD';
......
__END__
sub subroutine1 {......}
......
Обратите внимание, что имя импортированной подпрограммы должно быть указано явно, поскольку по умолчанию она не импортируется, хотя ее импорт и разрешен. (Подпрограммы и переменные типа AUTOLOAD, играющие специальную роль в Perl, не должны экспортироваться по умолчанию, поскольку велик риск смешивания идентификаторов.)
Использование модулей AutoSplit и AutoLoader позволяет упростить проблему создания прототипов подпрограмм — индексный файл autosplit.ix, созданный методом autosplit, загружается в момент выполнения команды use AutoLoader, (require AutoLoader подобного поведения не обеспечивает). Поскольку шаблоны вызова подпрограмм важны для проверки синтаксиса, модуль AutoLoader всегда загружается с помощью команды use, а не require. Кроме того, лучше поместить команду use AutoLoader в тело подпрограммы BEGIN, так как тогда прототипы подпрограмм станут известны Perl в первую очередь.
Вместо модулей AutoSplit и AutoLoader можно использовать также модуль SelfLoader. В этом случае определения подпрограмм должны размещаться после конструкции __DATA__ (вместо конструкции __END__), так что они также будут игнорироваться компилятором. При вызове этих подпрограмм модуль SelfLoader будет компилировать и загружать их. В следующем примере SelfLoader используется для динамической загрузки подпрограммы subroutine1 из модуля Module1:
package Module1;
BEGIN
{
use Exporter;
}
return 1;
END { }
Теперь вызовем подпрограмму subroutine1 модуля Module1 из другого модуля, — обратите внимание, что подпрограмма subroutine1 не компилируется и не загружается до первого вызова:
use Module1;
subroutine1();
Hello!
Рассмотрим, как работает модуль SelfLoader. Конструкция __DATA__, которая появилась впервые в Perl версии 5.001m, указывает, что код, предназначенный для компиляции, закончился. Содержимое файла от конструкции __DATA__ и до конца файла или до конструкции __END__, образует псевдофайл, доступный для чтения через дескриптор Пакет::DATA (здесь Пакет — это имя пакета, который обрабатывался в тот момент, когда была достигнута метка __DATA__). Module1 импортирует подпрограмму AUTOLOAD из модуля SelfLoader. При первом же обращении к подпрограмме, которая не определена в данном модуле, AUTOLOAD откроет псевдофайл, прочитает его содержимое, скомпилирует и загрузит в память все подпрограммы, которые встретятся ей при чтении. Соответственно, при последующих обращениях подпрограммы будут уже определены и подпрограмма AUTOLOAD вызываться не будет.
Использование SelfLoader позволяет избежать сложной иерархии файлов на диске и необходимости вызывать autosplit каждый раз, когда в текст модуля вносятся изменения (а также выполнять поиск нужного файла при первом вызове подпрограммы). За это приходится платить более долгим временем загрузки, так как строки текста, следующие за конструкцией __DATA__, обрабатываются синтаксическим анализатором (как данные, а не как компилируемый код). Недостатком модуля SelfLoader является то, что он загружает сразу все подпрограммы модуля и не позволяет определять для модуля собственную подпрограмму AUTOLOAD (она всегда импортируется из модуля SelfLoader). Преимуществом же модуля AutoLoader является более быстрая загрузка модуля и отсутствие лишних данных в памяти.