home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

Perl/codepiece/use_require01 (v1)

Perl/codepiece/use_require01 (v1)

Perl / codepiece / use_require01 (v1)
id: 113 所有者: msakamoto-sf    作成日: 2007-03-16 22:27:28
カテゴリ: Perl 

他のPerlコード、特にパッケージ(.pm)をロードするには "use" 或いは "require" が使用される。
一般に"use"はコンパイル時に評価され、"require"は実行時に評価されると言われている。

ここでは、BEGIN/CHECK/INIT/END ブロックでprintさせることで、実際にどちらがどの段階でどう処理されるのかを浅いレベルで確認する。

use 使用時のBEGIN/CHECK/INIT/END ブロック

まず、普通にuseを使用したときのBEGIN/CHECK/INIT/ENDブロックの呼ばれる順序を確認する。前掲のperlmodによれば、各ブロックが複数回定義されている場合以下の順序で呼ばれるはずである。

  • BEGIN : コンパイル時に、出現するたびに即座に呼ばれる。
  • CHECK : コンパイル終了後、実行が始まる前に、LIFO(Last In, Fast Out)で呼ばれる。
  • INIT : 実行が始まる前に、FIFO(Fast In, Fast Out)で呼ばれる。
  • END : 実行が終了し、Perlインタプリタが終了するタイミングで、LIFOで呼ばれる。

また、useする方でも複数箇所にBEGIN - ENDを仕掛け、useする/される側でどういった順序でBEGN - ENDブロックがコールされていくのかも確認してみる。

  • コードピース
    • test01.pl
#!/usr/bin/perl
use strict;
use warnings;
use File::Basename;

# @INCの調整
BEGIN {
	my (undef, $__base_dir, undef) = fileparse($0);
	push(@INC, $__base_dir);
	print __PACKAGE__, " BEGIN (1)\n";
}
CHECK { print __PACKAGE__, " CHECK (1)\n"; }
INIT  { print __PACKAGE__, " INIT  (1)\n"; }
END   { print __PACKAGE__, " END   (1)\n"; }

use Hoge::Bohe;

BEGIN { print __PACKAGE__, " BEGIN (2)\n" }
CHECK { print __PACKAGE__, " CHECK (2)\n" }
INIT  { print __PACKAGE__, " INIT  (2)\n" }
END   { print __PACKAGE__, " END   (2)\n" }

BEGIN { print __PACKAGE__, " BEGIN (3)\n" }
CHECK { print __PACKAGE__, " CHECK (3)\n" }
INIT  { print __PACKAGE__, " INIT  (3)\n" }
END   { print __PACKAGE__, " END   (3)\n" }

&Hoge::Bohe::bohe();
print "end.\n";
    • Hoge/Bohe.pm
package Hoge::Bohe;
use strict;
use warnings;

BEGIN { print __PACKAGE__, " BEGIN (1)\n" }
INIT  { print __PACKAGE__, " INIT  (1)\n" }
CHECK { print __PACKAGE__, " CHECK (1)\n" }
END   { print __PACKAGE__, " END   (1)\n" }

sub bohe {
	print "Here in Hoge::Bohe::bohe();\n";
}

BEGIN { print __PACKAGE__, " BEGIN (2)\n" }
INIT  { print __PACKAGE__, " INIT  (2)\n" }
CHECK { print __PACKAGE__, " CHECK (2)\n" }
END   { print __PACKAGE__, " END   (2)\n" }

BEGIN { print __PACKAGE__, " BEGIN (3)\n" }
INIT  { print __PACKAGE__, " INIT  (3)\n" }
CHECK { print __PACKAGE__, " CHECK (3)\n" }
END   { print __PACKAGE__, " END   (3)\n" }

1;
__END__
  • 出力:ここでは特に、コンパイルのみのときと実行まで含めたとき、二つの出力結果を示す。
  • コンパイルのみ
$ perl -c test01.pl
main BEGIN (1)        # BEGINが出現した順番に呼ばれている。
Hoge::Bohe BEGIN (1)  # test01.plから見て、途中でuseが入っていても、
Hoge::Bohe BEGIN (2)  # 純粋にBEGINの出現順に呼ばれている。
Hoge::Bohe BEGIN (3)
main BEGIN (2)
main BEGIN (3)
main CHECK (3)        # CHECKがLIFOで呼ばれている。
main CHECK (2)        # -> このネストも純粋に"出現した順のLIFO"で
Hoge::Bohe CHECK (3)  #    呼ばれている。
Hoge::Bohe CHECK (2)
Hoge::Bohe CHECK (1)
main CHECK (1)
test01.pl syntax OK
  • 実行まで含める
$ ./test01.pl
main BEGIN (1)
Hoge::Bohe BEGIN (1)
Hoge::Bohe BEGIN (2)
Hoge::Bohe BEGIN (3)
main BEGIN (2)
main BEGIN (3)
main CHECK (3)
main CHECK (2)
Hoge::Bohe CHECK (3)
Hoge::Bohe CHECK (2)
Hoge::Bohe CHECK (1)
main CHECK (1)
main INIT  (1)          # INITがFIFOで呼ばれている。
Hoge::Bohe INIT  (1)    # これも純粋に"出現順のFIFO"のようである。
Hoge::Bohe INIT  (2)    # useされる/する側のネスト構造とは無関係に、
Hoge::Bohe INIT  (3)    # それこそlexicalにFIFOで呼ばれている。
main INIT  (2)
main INIT  (3)
Here in Hoge::Bohe::bohe();
end.
main END   (3)          # ENDもINIT同様に、こちらはLIFOで呼ばれている。
main END   (2)
Hoge::Bohe END   (3)
Hoge::Bohe END   (2)
Hoge::Bohe END   (1)
main END   (1)

以上より、BEGIN, CHECKはperldocの通りコンパイル時に処理され、INIT, ENDは実行時に処理されること。およびその順序がperldocの通りであることを確認できた。

useではなくrequireを使いたいとき

実際にrequireの実験に入る前、なぜuseではなくrequireを使うのか、使う時について簡単にメモしておく。

結論から言うと、パッケージ名を設定ファイルなどで外部化したい時、useのパッケージ名は変数を受け付けないからである。

例えば以下のコードはコンパイルエラーとなる。

my $pkg_name = "Hoge::Bohe";
use $pkg_name;


syntax error at ****.pl line XX, near "use $pkg_name"
****.pl had compilation errors.

しかしrequireであれば、パッケージ名に変数を使用できる。結局、Factoryを介してロードするモジュールを切り替えたい時は、useではなくrequireしか利用できない。こういった局面ではrequireの方が効果的である。

require 使用時のBEGIN/CHECK/INIT/END ブロック

Hoge::Bohe.pmの方は変更せず、test01.pl の use を require にして確認してみる。

  • コードピース (test01.pl)
use Hoge::Bohe;
→
require Hoge::Bohe;   # 注意!! require "Hoge::Bohe"ではない。"bare-word"であること。
  • 出力
  • コンパイル時
$ perl -c test01.pl
main BEGIN (1)  # Hoge::BoheのBEGIN, CHECKが呼び出されていない
main BEGIN (2)  #  = コンパイルされていない。
main BEGIN (3)
main CHECK (3)
main CHECK (2)
main CHECK (1)
test02.pl syntax OK
  • 実行時
$ ./test02.pl
main BEGIN (1)
main BEGIN (2)
main BEGIN (3)
main CHECK (3)
main CHECK (2)
main CHECK (1)
main INIT  (1)
main INIT  (2)
main INIT  (3)
Hoge::Bohe BEGIN (1)
Too late to run INIT block at Hoge/Bohe.pm line 6.
Too late to run CHECK block at Hoge/Bohe.pm line 7.
Hoge::Bohe BEGIN (2)
Too late to run INIT block at Hoge/Bohe.pm line 15.
Too late to run CHECK block at Hoge/Bohe.pm line 16.
Hoge::Bohe BEGIN (3)
Too late to run INIT block at Hoge/Bohe.pm line 20.
Too late to run CHECK block at Hoge/Bohe.pm line 21.
Here in Hoge::Bohe::bohe();
end.
Hoge::Bohe END   (3)
Hoge::Bohe END   (2)
Hoge::Bohe END   (1)
main END   (3)
main END   (2)
main END   (1)

Hoge::BoheのINIT, CHECKが実行されていない。 代わりにWarningが出力されている。perldiag(http://perldoc.perl.org/perldiag.html)参照。
ちなみにHoge::BoheからINIT, CHECKを除去すればwarningは出力されなくなる。

また require の引数に "bare-word" を使用している点に注目する。ここを下記の用にquoted-stringにすると、エラーとなる。

require "Hoge::Bohe";


...
main INIT  (3)
Can't locate Hoge::Bohe in @INC (@INC contains: ... ) at test02.pl line 18.
main END   (3)
...

perldiagには(F)、すなわちトラップ可能なFatal Errorとして扱われている。とにもかくにも、エラーでPerlインタプリタ自体は終了し、ENDブロックの実行に進んでいる。

実は、これは本当にrequireを使いたい "パッケージ名の外部化" も同様のwarningがでてしまうことを意味する。

my $pkg_name = "Hoge::Bohe";
require $pkg_name;

これは上記と同じ結果になる。

というのも、bare-wordの場合、Perl側で "::" をディレクトリセパレータにし、拡張子に勝手に.pmを付けてくれる。すなわち、

require Hoge::Bohe;
イコール
require "Hoge/Bohe.pm";

が成立する。というわけで、quoted stringでパッケージ名を指定してしまうと、本当に"Hoge::Bohe"というファイル名を見に行ってしまう。

もちろん、これを回避する方法はしっかりとperldocに記載されている。 requireのperldoc(http://perldoc.perl.org/functions/require.html)を見ると、以下のような回避策が有効とされている。

eval "require $pkg_name";

実際、これで test02.pl も問題なく Hoge/Bohe.pm をロードできた。(ただし、もちろんCHECK, INITのwarning付き)



プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2008-12-24 22:31:05
md5:8381250d562d2f6d1347166b57795f16
sha1:aae8a5391f1ef7d263b872b08fd75956180556c1
コメント
コメントを投稿するにはログインして下さい。