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

8 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;
Run Code Online (Sandbox Code Playgroud)

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();
Run Code Online (Sandbox Code Playgroud)

输出:

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:
Run Code Online (Sandbox Code Playgroud)

我期望$greeting并且$testmodule::greeting因为子程序的包是一样的testmodule.

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

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

Dav*_* W. 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";
Run Code Online (Sandbox Code Playgroud)

更好的是:

{
   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
Run Code Online (Sandbox Code Playgroud)

如果不放入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;
Run Code Online (Sandbox Code Playgroud)

文件 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
Run Code Online (Sandbox Code Playgroud)

而且,输出是:

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"
Run Code Online (Sandbox Code Playgroud)

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

有时,它可能有点棘手:

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

print "Foo = $foo\n";
Run Code Online (Sandbox Code Playgroud)

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

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

print "Foo = $foo\n"; #This works!
Run Code Online (Sandbox Code Playgroud)

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


小智 8

在Perl中读取变量范围:更仔细的基础知识后,我意识到声明的变量my不在当前包中.例如,如果我声明my $var = "hello" $main::var仍然没有值,则在没有模块的简单脚本中.

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

如果我不这样做use strict并且不宣布my $greeting,它就像我预期的那样工作.获得预期价值而不是破坏的另一种方法use strict是使用our $greeting.不同之处在于,声明当前作用域中的变量,而我们在当前包中声明了一个变量.