Perl包变量何时超出范围?

z18hc3ub  于 2023-02-05  发布在  Perl
关注(0)|答案(2)|浏览(155)

从我的主程序中,我需要一个包含包的文件,然后从那个包中调用一个子例程:

while($somecondition){
  require( 'people.pm' );
  my $result = PERSON::stuff($args);
}

PERSON包声明了多个subs和一些'our'变量:

package PERSON;
our $name;
our ...
sub stuff {
  ...
}

在我的理解中,其他更多的面向对象语言需要声明一个新的对象示例,可能需要它自己的构造函数/初始化函数来使用“包”变量,但Perl似乎不是这样。
我处理的是遗留代码,所以我不想做太多修改,我只想了解包变量($name)是什么时候出现的,以及从主程序的Angular 来看,它们是什么时候返回到内存的。
在while循环后调用PERSON::stuff()会不会产生新的包变量?在调用包内的单个函数后,包变量会一直存在到程序结束吗?

s4n0splo

s4n0splo1#

这个问题混淆了一些概念,所以让我们首先解决看起来是主要问题的问题:如果一个包在某个范围内是require 'd,那么在该范围之外又是什么呢?
简而言之,包中的(动态全局)符号可以通过它们的完全限定名在require 'd的单元中的任何地方访问。†
让我们举个例子

use warnings;
use strict; 
use feature 'say';

TEST_SCOPE: {
    say "In scope, in ", __PACKAGE__;
    require TestPack;
    #hi();                  # "Undefined subroutine &main::hi called..."
    TestPack::hi();         # ok
    #say $global;           # $global ... who??
    say $TestPack::global;  # ok
    say "Leaving scope\n";
};

say "--- in ", __PACKAGE__;
TestPack::hi();             # ok
say $TestPack::global;      # ok

文件TestPack.pm

package TestPack;

use warnings;
use strict;
use feature 'say';

#use Exporter qw(import);  # This is normally done to export symbols
#our @EXPORT_OK = qw(hi);  # (unless the package is a class)

our $global = 7;

sub hi { say "hi from ", __PACKAGE__ }

1;

我们需要为这些符号使用完全限定的名称,因为它们没有被导入。如果包导出符号,我们导入一些‡,那么它们会进入调用包的名称空间,所以在上面的例子中,它们会在main::中可用,所以解释器中的任何代码都可以通过它们导出的名称访问它们(hi,不需要TestPack::hi).不能从那个包访问词法变量(用myourstate创建)§.
如果我们引入另一个包,而不是仅仅块(名为TEST_SCOPE),并且require是我们的TestPack,那么这也是有效的。

...    
package AnotherPack {
    require TestPack;
    ...
    1;
};
...
TestPack::hi();             # ok
...

(That package实际上应该在BEGIN块中,这并不改变这里的要点。)TestPack中的全局符号仍然可以在main::中通过它们的完全限定名访问。导出的名称,我们与require沿着导入,然后可以在 * 此包 * 中使用,但在main::中不可用。

  • 对问题的评论 *
  • 软件包名称(PERSON)及其文件名(person.pm)* 必须一致 *。例如,命名空间(==软件包)Person在文件Person.pm中定义
  • 这是关于require的基础知识--使用package;它与面向对象的概念无关。(尽管如此,类 * 首先是 * 一个包。参见perlootutperlobj。)也可以参见perlmod和our中的 Packages
  • 如果你使用一个bless-es包,返回的对象(示例)是一个词法变量(赋值给)。因此,它不存在于作用域之外。尽管包本身是可见的,正如上面所示,但在面向对象的工作中,我们不想戳到包的内部,而是通过对象使用方法。

所以是的,要在require-艾德作用域之外使用这个包,你需要在另一个作用域中示例化一个对象,这仍然和上面的例子很像--我们 * 可以 * 在需要它的作用域之外使用这个包名来示例化一个对象(try!),即使我会提出这样的设计问题(见下一节)

  • 这暗示了一个复杂的设计,在运行时(通过require)将包引入作用域;上下文是什么?(我希望它不是真的在while循环中。)

†打印出main的符号表%main::(例如使用Data::Dumper),我们得到

"TestPack::" => *main::TestPack::

沿着来自TestPack命名空间的所有其他符号。
‡如果一个包导出符号,我们require这个包,那么我们可以导入

require Pack::Name;
Pack::Name->import( qw(subname anothername ...) );

§注意our创建了一个词法变量,它是包变量的别名,* 是 * 可访问的。

uwopmtnx

uwopmtnx2#

zdim's answer很好地解释了包变量是如何工作和使用的,但我不认为它直接回答了它们何时超出范围的问题。
简而言之:

  • 包变量是全局静态变量,只是命名空间,所以"全局"方面并不可怕。
  • 与任何静态变量一样,它们在程序的整个执行过程中都在作用域中

你还问:
在我的理解中,其他更多的面向对象语言需要声明一个新的对象示例,可能需要它自己的构造函数/初始化函数来使用"包"变量,但Perl似乎不是这样。
包变量与Perl中的面向对象编程完全无关,它们不用于存储示例数据(除了有时候由内而外的对象,尽管这是一个更高级的主题)。

相关问题