ArininAV

Глава 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, например:

use Cwd;
$curdir = cwd;
print "$curdir\n";

Эта программа выводит на экран имя текущего каталога. Директива use Cwd добавляет имена из пакета Cwd к пространству имен нашей программы, поэтому мы можем обращаться к функции cwd() так, как если бы она была описана в нашей программе. При желании мы можем импортировать из пакета только отдельные его функции, например:

use Cwd 'chdir';     # импорт chdir из пакета Cwd
chdir "/temp";       # вызов функции chdir пакета Cwd
Cwd::chdir "/temp";  # то же самое
CORE::chdir "/temp"; # вызов встроенной функции chdir

Этот пример демонстрирует применение квалификатора пакет::имя. По сути дела, такая конструкция указывает компилятору, какое пространство имен нужно использовать. PERL поддерживает два специальных имени пакетов:

  • main — пространство имен главной программы. Принято по умолчанию, т. е. $::var эквивалентно $main::var.
  • CORE — пространство имен встроенных функций. Подробнее о нем см. п. 6.6.9.

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 — ключевое слово, указывающее вид возвращаемой информации:

Конструкция Значение
*name{PACKAGE} Имя пакета, в котором определено name.
*name{NAME} Имя элемента (т. е. name).
*name{SCALAR} Ссылка на скалярную переменную $name.
*name{ARRAY} Ссылка на массив @name.
*name{HASH} Ссылка на ассоциативный массив %name.
*name{CODE} Ссылка на подпрограмму &name.
*name{IO}, *name{FILEHANDLE} Ссылка на указатель файла с именем name.
*name{GLOB} Ссылка на typeglob для name.

Пример:

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 выполняет следующие действия:

  • Если выражение является словом, то к нему добавляется расширение .pm, а все :: в нем заменяются на символ /.
  • Если полученное имя файла уже содержится в ассоциативном массиве %INC, то функция возвращает 1.
  • В противном случае функция просматривает каталоги, заданные в массиве @INC, в поисках заданного файла. Если такого файла нет, то программа завершается с соответствующей диагностикой.
  • Если файл найден, то выполняется операция do имя_файла. Если она возвращает ложь, то программа завершается с соответствующей диагностикой, иначе функция возвращает результат операции do.

Иными словами, функция require находит заданный файл пакета и выполняет его. Пакет обязан вернуть истину, указывая, что его инициализация прошла успешно, поэтому принято заканчивать любой пакет строкой 1;, чтобы не забыть об этом требовании. Обратите внимание, что пакет загружается только один раз. Пример: оператор

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 имя список

Эта функция вызывает метод unimport имя список или завершается по фатальной ошибке, если модуль не имеет этого метода.

В общем случае использование функции 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а можно разбить на следующие категории:

  • Директивные модули (pragmatic modules). Это модули, которые играют роль директив, управляющих компилятором. Например, директива use integer просто загружает директивный модуль integer, а директива no integer отключает его использование. Полный список директивных модулей приведен в Таблице П23.1.
  • Стандартные модули (standard modules). Это модули, которые тщательно оттестированы и включены во все дистрибутивы PERLа, поэтому мы можем быть уверены, что их использование не вызовет проблем при переносе программ на другой компьютер (если, конечно, там установлена версия PERLа не ниже нашей). Полный список стандартных модулей приведен в Таблице П23.2.
  • Прочие модули, набор которых варьируется для разных дистрибутивов. Если прагматические и стандартные модули размещаются в каталоге lib, то эти модули традиционно размещаются в каталоге site/lib. Помните, что они не обязаны присутствовать на всех компьютерах, где установлен PERL. Если нужного модуля на вашем компьютере не оказалось, то вы всегда можете загрузить его из CPAN.

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 ();                     # не импортировать ничего

Для удобства импорта введено следующее соглашение: если первое имя в списке импорта начинается с символа !, : или /, то этот список рассматривается как набор правил, управляющих списком импортируемых имен. Правила обрабатываются слева направо в соответствии со следующей таблицей:

Конструкция Значение
name Добавить имя name.
!name Удалить имя name.
:DEFAULT Добавить все имена из списка @EXPORT.
!:DEFAULT Удалить все имена из списка @EXPORT.
:tag Добавить все имена из списка %EXPORT_TAGS{tag}.
!:tag Удалить все имена из списка %EXPORT_TAGS{tag}.
/pattern/ Добавить все имена из списков @EXPORT и @EXPORT_OK, соответствующие образцу pattern.
!/pattern/ Удалить все имена из списков @EXPORT и @EXPORT_OK, соответствующие образцу pattern.

Если список начинается с операции удаления, то считается, что ей предшествует операция :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);

Для отладки подобных конструкций удобно добавить в свою программу оператор BEGIN { $Exporter::Verbose=1 }, который выводит на консоль информацию об обработке операций импорта.

Помимо описанных массивов модуль 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, @_); }

Тогда оператор use Sample ($b) экспортирует переменную $Sample::b на один уровень вверх, т. е. в программу или модуль, вызвавшие модуль Sample.

Наконец, Exporter позволяет нам явно запретить экспорт каких-либо символов. Имена таких символов должны быть указаны в массиве @EXPORT_FAIL. При попытке импортировать эти символы Exporter вызывает оператор

@failed_symbols = $module_name->export_fail(@failed_symbols);

Если список, возвращаемый методом export_fail, пуст, то запрошенные символы экспортируются. В противном случае для каждого символа в этом списке генерируется соответствующее сообщение об ошибке. Мы можем переопределить в своем модуле метод export_fail или воспользоваться его реализацией по умолчанию, которая просто возвращает переданный ей список.