簡體   English   中英

使用typeglob重新定義perl函數不能按預期工作

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM