“导出器”,“使用变量”和“本地”

时间:2015-08-06 19:18:28

标签: perl scope

我试图将此行为归结为最简单的测试用例。考虑以下两个模块:

Bar.pm

package Bar;

use base 'Exporter';
use vars qw/ $BarVar /;
BEGIN { @EXPORT_OK = qw/ $BarVar /; }

$BarVar = 'original';

1;

Foo.pm

package Foo;

use Bar qw/ $BarVar /;

sub foo { print $BarVar . "\n"}

1;

现在,输出以下脚本 -

use strict;
use warnings;

use Foo;

{
  local $Bar::BarVar = 'modified'; 
  Foo::foo();
}

Foo::foo();

是“原始”打印两次,我希望它被“修改”后跟“原始”,因为我希望local声明在整个过程中替换包变量$Bar::BarVar它的整个范围,包括对foo()的第一次调用。解释是什么?我如何在本地覆盖$Bar::BarVar

2 个答案:

答案 0 :(得分:9)

您可能听说我说myour创建变量(后者是别名),但local只是暂时备份该值。

我撒谎了。

local会进行备份,但不会进行备份。它备份相关标量的地址,创建新标量,并将名称与新标量相关联。在这种情况下,$Bar::BarVar引用新标量,$Foo::BarVar引用旧标量。

$ perl -E'
   *x = \$y;  say \$x, " - ", \$y;
   local $y;  say \$x, " - ", \$y;
'
SCALAR(0x44d7c70) - SCALAR(0x44d7c70)
SCALAR(0x44d7c70) - SCALAR(0x44ba130)

如果您只是实际备份该值,问题就会消失。

use Sub::ScopeFinalizer qw( scope_finalizer );

{
   my $backup = $Bar::BarVar;
   my $guard = scope_finalizer { $Bar::BarVar = $backup };
   $Bar::BarVar = 'modified'; 
   Foo::foo();
}

可能存在更专业的工具。

答案 1 :(得分:1)

请参阅What Not to Export

  

还有一个项目要添加到此列表中。不要导出变量名称。仅仅因为出口商允许你这样做,这并不意味着你应该这样做。

 @EXPORT_OK = qw($svar @avar %hvar); # DON'T!
     

导出变量不是一个好主意。它们可以在引擎盖下发生变化,在远处引发可怕的效果,这些效果太难以追踪和修复。相信我:他们不值得。

     

为了提供设置/获取类范围设置的功能,最好将访问器设置为子例程或类方法。

此规则有一个例外:如果您有常量变量(例如使用Const::Fast创建的变量),则可以导出它们,因为根据定义,它们无法在远处创建效果。