知ってそうで意外と知られていないperlの小技 10選という記事にある「モジュールのメソッドを上書きする」という項について考えてみました。記事にあるやり方では、
となっていたのですが、こうするよりも、上書きが必要な箇所のみをブロック({ ... }
)で囲み、その中でlocal
を使って上書きする方がよいのでは、と思ったのです。具体的に書くと以下の通り。
use strict;
use warnings;
#----------
package Foo;
sub something { print "something original.\n" };
#----------
package main;
Foo->something();
{
no warnings 'redefine';
local *Foo::something = sub { print "something overwitten.\n" };
Foo->something();
}
Foo->something();
実行結果は以下の通り。
something original. something overwitten. something original.
こうすると本来のメソッドのリファレンスを保存する手間も省けるし、メソッド上書きと no warnings 'redefine'
の効果の範囲も分かり易いのではないかと思います。
……ということを考えていたのですが、後日、Re: 知ってそうで意外と知られていないperlの小技 10選という別の記事で更に別のやり方が提示されていました。
事前にundefすると、 no warnings 'redefine' しなくとも警告は出ません。
use strict; use warnings; sub do_something { ... } undef &do_something; *do_something = sub { print "Implemented.\n" }; do_something;
サブルーチンをundef
で未定義にできる、ということを知らなかったのでなるほどと思ったのですが、これをブロック内のlocal
上書きでもできるだろうか、というところが気になり、試してみました。
use strict;
use warnings;
#----------
package Foo;
sub something { print "something original.\n" };
#----------
package main;
Foo->something();
{
undef &Foo::something;
local *Foo::something = sub { print "something overwitten.\n" };
Foo->something();
}
Foo->something();
結果は以下の通り。
something original. something overwitten. Undefined subroutine &Foo::something called at b.pl line 18.
ブロック内であってもundef
の効果はグローバルに及んでしまうので、本来のメソッドを保持できなくなっていました。
しかしlocal
で宣言だけするなら、宣言した変数は未定義になることを思い出し、
local *Foo::something;
*Foo::something = sub { print "something overwitten.\n" };
とすればよいのでは、と思いつきました。これは一応意図通りに動いたのですが、型グロブでまとめてlocal
宣言するということは、同じ名前を持つスカラ・配列・ハッシュ等の変数にも効果が及ぶということでもあります。以下のコードを試してみました。
use strict;
use warnings;
#----------
package Foo;
sub something { print "something original.\n" };
our $something = 'SOME STRING';
#----------
package main;
Foo->something();
print $Foo::something, "\n";
{
local *Foo::something;
*Foo::something = sub { print "something overwitten.\n" };
Foo->something();
print $Foo::something, "\n";
}
Foo->something();
print $Foo::something, "\n";
結果は以下の通り。
something original. SOME STRING something overwitten. Use of uninitialized value $Foo::something in print at b.pl line 19. something original. SOME STRING
メソッドと同じ名前のスカラ変数($Foo::something
)もまとめて未定義にされていました。なのでlocal
宣言と上書きを分けて書くのは注意が必要かもしれません。
ちなみに、local
宣言と同時にコードリファレンスを代入してやると、他の変数が上書きされるようなことにはなりませんでした。動作としてはやや不思議な気もしますが、これが一番問題の無い書き方のように思います。
use strict;
use warnings;
#----------
package Foo;
sub something { print "something original.\n" };
our $something = 'SOME STRING';
#----------
package main;
Foo->something();
print $Foo::something, "\n";
{
no warnings 'redefine';
local *Foo::something = sub { print "something overwitten.\n" };
Foo->something();
print $Foo::something, "\n";
}
Foo->something();
print $Foo::something, "\n";
結果は以下の通り。ブロック内でも$Foo::something
の値は保持されていました。
something original. SOME STRING something overwitten. SOME STRING something original. SOME STRING
関連: