模块子程序中的包变量范围

时间:2012-04-02 12:51:50

标签: perl perl-module

如何更改模块使用的包中的变量值,以便该模块中的子程序可以使用它?

这是我的测试用例:

testmodule.pm:

package testmodule;

use strict;
use warnings;
require Exporter;

our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);

@ISA = qw(Exporter);
@EXPORT = qw(testsub);

my $greeting = "hello testmodule";
my $var2;

sub testsub {
    printf "__PACKAGE__: %s\n", __PACKAGE__;
    printf "\$main::greeting: %s\n", $main::greeting;
    printf "\$greeting: %s\n", $greeting;
    printf "\$testmodule::greeting: %s\n", $testmodule::greeting;
    printf "\$var2: %s\n", $var2;
} # End testsub
1;

testscript.pl:

#!/usr/bin/perl -w
use strict;
use warnings;
use testmodule;

our $greeting = "hello main";
my $var2 = "my var2 in testscript";

$testmodule::greeting = "hello testmodule from testscript";
$testmodule::var2 = "hello var2 from testscript";

testsub();

输出:

Name "testmodule::var2" used only once: possible typo at ./testscript.pl line 11.
__PACKAGE__: testmodule
$main::greeting: hello main
$greeting: hello testmodule
$testmodule::greeting: hello testmodule from testscript
Use of uninitialized value $var2 in printf at testmodule.pm line 20.
$var2:

我希望$greeting$testmodule::greeting相同,因为子例程的包是testmodule

我想这与use d模块eval d的方式有关,就好像在BEGIN块中一样,但我想更好地理解它。

我希望从主脚本设置变量的值,并在模块的子例程中使用它,而不使用变量的完全限定名称。

2 个答案:

答案 0 :(得分:11)

正如您所知,当您使用my时,您正在创建一个本地范围的非包变量。要创建包变量,请使用our而非my

my $foo = "this is a locally scoped, non-package variable";
our $bar = "This is a package variable that's visible in the entire package";

更好:

{
   my $foo = "This variable is only available in this block";
   our $bar = "This variable is available in the whole package":
}

print "$foo\n";    #Whoops! Undefined variable
print "$bar\n";    #Bar is still defined even out of the block

如果未在程序中放置use strict,则定义的所有变量都是包变量。这就是为什么当你没有把它,它按照你认为的方式工作,并把它放在打破你的程序。

但是,正如您在以下示例中所看到的,使用our将解决您的困境:

档案Local/Foo.pm

#! /usr/local/bin perl
package Local::Foo;

use strict;
use warnings;
use feature qw(say);

use Exporter 'import';
our @EXPORT = qw(testme);

our $bar = "This is the package's bar value!";
sub testme {

    # $foo is a locally scoped, non-package variable. It's undefined and an error
    say qq(The value of \$main::foo is "$main::foo");

    # $bar is defined in package main::, and will print out
    say qq(The value of \$main::bar is "$main::bar");

    # These both refer to $Local::Foo::bar
    say qq(The value of \$Local::Foo::bar is "$Local::Foo::bar");
    say qq(The value of bar is "$bar");
}

1;

档案test.pl

#! /usr/local/bin perl
use strict;
use warnings;
use feature qw(say);
use Local::Foo;

my $foo = "This is foo";
our $bar = "This is bar";
testme;

say "";
$Local::Foo::bar = "This is the NEW value for the package's bar";
testme

而且,输出是:

Use of uninitialized value $foo in concatenation (.) or string at Local/Foo.pm line 14.
The value of $main::foo is ""
The value of $main::bar is "This is bar"
The value of $Local::Foo::bar is "This is the package's bar value!"
The value of bar is "This is the package's bar value!"

Use of uninitialized value $foo in concatenation (.) or string at Local/Foo.pm line 14.
The value of $main::foo is ""
The value of $main::bar is "This is bar"
The value of $Local::Foo::bar is "This is the NEW value for the package's bar"
The value of bar is "This is the NEW value for the package's bar"

您获得的错误消息是$foo作为局部变量的结果,因此在包内不可见。同时,$bar是一个包变量,可见。

有时,它可能有点棘手:

if ($bar -eq "one") {
   my $foo = 1;
}
else {
   my $foo = 2;
}

print "Foo = $foo\n";

这不起作用,因为$foo仅在if块内部有一个值。你必须这样做:

my $foo;
if ($bar -eq "one") {
   $foo = 1;
}
else {
  $foo = 2;
}

print "Foo = $foo\n"; #This works!

是的,最初可能会让你的脑袋缠绕起来,但use strict;use warnings;的使用现在已经得到了充分的理由。使用use strict;use warnings;可能已经消除了人们在Perl中犯下的90%的错误。您不能错误地在程序的一个部分中设置$foo的值,并尝试在另一个部分中使用$Foo。这是我在Python中真正想念的事情之一。

答案 1 :(得分:8)

在仔细阅读Variable Scoping in Perl: the basics后,我意识到用my声明的变量不在当前包中。例如,如果我声明my $var = "hello" $main::var仍然没有值,则在没有模块的简单脚本中。

在这种情况下适用的方式是在模块中。由于my $greeting在文件中声明,因此隐藏了包的版本$greeting,这是子例程看到的值。如果我不首先声明变量,子程序将看到包变量,但它没有那么远,因为我use strict

如果我没有use strict而且没有声明my $greeting,那么它就像我预期的那样有效。获得预期值而不是中断use strict的另一种方法是使用our $greeting。不同之处在于my在当前 范围 中声明变量,而our在当前 包中声明变量< /强>