[英]Redefine perl function using typeglob doesn't work as expected
這個例子工作正常:
use File::Slurp qw(read_file);
local *File::Slurp::read_file = sub {
return 'test';
};
warn File::Slurp::read_file('/root/test.txt'); # return 'test'
這個也是:
use File::Slurp qw(read_file);
local *read_file = sub {
return 'test';
};
warn read_file('/root/test.txt'); # return 'test'
但是如果我在typeglob中使用函數的全名它不起作用並嘗試讀取文件:
use File::Slurp qw(read_file);
local *File::Slurp::read_file = sub {
return 'test';
};
warn read_file('/root/test.txt');
任何人都可以解釋為什么我不能通過完整的命名空間, File::Slurp::read_file
重新定義子程序,並使用短名稱?
在對象方法的情況下,它工作正常:
use LWP::UserAgent;
local *LWP::UserAgent::get = sub {
return HTTP::Response->new( undef, undef, undef, 'Hello world' );
};
my $ua = LWP::UserAgent->new;
warn $ua->get()->content;
您的問題是由導出的工作方式引起的。 以及Perl如何為值分配名稱。 在Perl中,每個名稱都是一個值的鏈接,因此sub read_line { ... }
創建一個匿名子例程引用並將其分配給name &read_line
use File::Slurp qw(read_file);
local *File::Slurp::read_file = sub {
return 'test';
};
在第一個示例中,您將覆蓋File::Slurp::read_file
,然后調用File::Slurp::read_file
以便獲得File::Slurp::read_file
。
use File::Slurp qw(read_file);
local *read_file = sub {
return 'test';
};
在第二個示例中,您將覆蓋導入的read_file
版本,然后調用它以便獲得read_file
的版本
use File::Slurp qw(read_file);
local *File::Slurp::read_file = sub {
return 'test';
};
在第三個示例中,會發生以下情況:
use File::Slurp;
在編譯時執行*read_file = \\&File::Slurp::read_file
,這使得read_file
指向現有版本的File::Slurp::read_file
。 然后你的代碼為*File::Slurp::read_file
一個新的子引用,但是這不會改變read_file
,它仍然指向File::Slurp::read_file
最初指向的子引用。 然后調用read_file
,它指向File::Slurp::read_file
的原始導入版本
在你的第四個例子中,Perl的方法解析系統意味着你正在調用LWP::UserAgent::get
所以這相當於你的第一個例子。
導出子時,其引用將寫入調用者的符號表。 顯然,在重新定義模塊中的子模式后,調用者中的非限定名稱仍然引用“舊”,即導出,而不是重新定義的名稱。
一個明確的解決方法是在調用包中明確別名(非限定)名稱
*func = *Module::func = sub { ... };
然后將其包裝在子例程中,從中可以處理所有需要的命名空間
sub redefine_sub {
my ($fqn, $code) = @_;
no warnings 'redefine'; # these pragmas are lexical, and
no strict 'refs'; # so stay scoped to this sub only
*{ $fqn } = $code;
# Redefine in caller
my ($name) = $fqn =~ /.*::(.*)/;
my $to_caller = caller() . '::' . $name;
*{ $to_caller } = $code;
}
在來電者
use Module qw(func);
redefine_sub('Module::func', sub { ... });
一些原始嘗試,保留在這里,因為它們可能看似合理但不起作用
有人可能認為(或者,我做過)切換定義的順序應該有效
use warnings;
use strict;
use feature 'say';
BEGIN { # must come first, in BEGIN block
no warnings 'redefine';
*Cwd::cwd = sub { return 'impostor' };
};
use Cwd;
say "cwd(): ", cwd();
這確實打印了impostor
。 但是,比要求特定的定義順序並且不再允許local
更糟糕,這對File::Slurp
也不起作用 。 我沒有看到這些模塊的來源有什么不同。
它適用於在同一文件中定義的簡單明了的普通模塊
use warnings;
use strict;
use feature 'say';
BEGIN {
package TestRedef;
use Exporter qw(import);
our @EXPORT = qw(hi);
sub hi { return "\thi from " . __PACKAGE__; }
$INC{'TestRedef.pm'} = 1;
};
### in main::
BEGIN {
no warnings 'redefine';
*TestRedef::hi = sub { return 'impostor in ' . __PACKAGE__ };
};
use TestRedef;
say hi();
但如果這個包在一個單獨的文件中給出,它又不起作用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.