使用typeglob重新定义perl函数不能按预期工作



此示例运行良好:

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 { ... }创建了一个匿名子例程引用,并将其分配给名称&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最初指向的子引用。然后,您调用指向File::Slurp::read_file的原始导入版本的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 { ... });

(原始尝试,效果不佳,已被删除(

最新更新