пакетная область видимости в подпрограмме модуля - PullRequest
8 голосов
/ 02 апреля 2012

Как изменить значение переменной в пакете, используемом модулем, чтобы его могли использовать подпрограммы в этом модуле?

Вот мой тестовый пример:

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 ]

11 голосов
/ 02 апреля 2012

Как вы узнали, когда вы используете my, вы создаете локальную переменную non-package .Чтобы создать переменную пакета, вы используете 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;, вероятно, устранило 90% ошибок, которые люди делают в Perl.Вы не можете ошибиться, установив значение $foo в одной части программы и попытавшись использовать $Foo в другой.Это одна из вещей, которые мне действительно не хватает в Python.

8 голосов
/ 02 апреля 2012

После прочтения Области видимости переменной в Perl: основы более внимательно я понял, что переменная, объявленная с my, отсутствует в текущем пакете. Например, в простом сценарии без модулей, если я объявляю, my $var = "hello" $main::var все еще не имеет значения.

Способ, которым это применимо в этом случае, заключается в модуле. Так как my $greeting объявлено в файле, это скрывает версию пакета $greeting, и это значение, которое видит подпрограмма. Если я сначала не объявлю переменную, подпрограмма увидит переменную пакета, но это не так далеко, потому что я use strict.

Если я не use strict и не объявляю my $greeting, это работает так, как я и ожидал. Другой способ получить желаемое значение, а не разбить use strict - использовать our $greeting. Разница в том, что my объявляет переменную в текущем scope , а our объявляет переменную в текущем пакет .

...