2

这是一个有趣的 Perl 行为。(至少对我来说:))

我有两个包PACKAGE1PACKAGE2它们导出具有相同名称的函数Method1()

由于将有很多包会导出相同的功能,所以use在 Perl 文件中添加所有内容将是乏味的。所以,我创建了一个包含INCLUDES.pm这些文件的通用包含文件use

包括.pm:

use PACKAGE1;
use PACKAGE2;

1;

包装1.pm:

package PACKAGE1;

use Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw (
          Method1
);

sub Method1{
print "PACKAGE1_Method1 \n";
}

1;

包2.pm:

package PACKAGE2;

use Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw (
    Method1
);

sub Method1{
    print "PACKAGE2_Method1 \n";
}

1;

测试.pl:

##################first package################
package Test1;
use INCLUDES;
my @array = values(%INC);
print  "@array  \n";

Method1();

##################second package################
package Test2;
use INCLUDES;  #do "INCLUDES.pm";
my @array = values(%INC);
print  "@array  \n";

Method1();

Method1()动机是,在任何 Perl 文件中只能使用最新的包。

输出让我吃惊。我希望这两个Method1()电话都Tests.pl应该成功。但只有第一个Method1()执行,第二个Method1()调用说“未定义”。

输出:

C:/Perl/site/lib/sitecustomize.pl PACKAGE1.pm C:/Perl/lib/Exporter.pm PACKAGE2
.pmINCLUDES.pm

PACKAGE2_Method1

C:/Perl/site/lib/sitecustomize.pl PACKAGE1.pm C:/Perl/lib/Exporter.pm PACKAGE2
.pm INCLUDES.pm

Undefined subroutine &Test2::Method1 called at C:\Temp\PackageSample\Tests.pl line 15.

有人对此有任何答案/看法吗?

实际场景:

多个 Perl 模块中的方法将具有相同的名称。但是只能使用高优先级 perl 模块中的方法。

例如,如果PACKAGE1contains Method1(), Method2()& PACKAGE2contains only Method1()Method1()则应使用 from PACKAGE2&Method2()应使用 fromPACKAGE1

基本上我想在基于 Preference 的模块之间实现层次结构。有什么办法吗?

4

2 回答 2

4

在 Perl 中,use Module相当于

BEGIN { require Module; Module->import; }

但是require缓存了需要的模块列表。每个 Perl 进程只加载一次模块。所以只有第一个use IMPORTS做任何事情。由于您的IMPORTS模块没有import方法,因此当您use再次使用它时不会发生任何事情。

我不太确定您要完成什么。也许您的 IMPORTS 模块应该是一个实际的包,具有import导出您想要的任何功能的方法。这样,每个use IMPORTS函数都会将函数导出到调用它的包中。

于 2013-02-02T05:43:11.310 回答
3

use MyPackage相当于BEGIN{ require MyPackage; MyPackage->import }。从 Exporter 继承设置了一个import执行“别名”功能的类方法。

问题是您包含模块没有正确重新导出模块。这很重要,因为这是将函数导入调用者命名空间的过程。虽然这并不难自己制作,但有一个方便的模块用于此目的Import::Into

这是一个包含在单个文件中的示例,应该很容易重新扩展为多个文件,唯一重要的区别在于 Includes 模块。我做了一些其他表面上的改变,但这些更符合我的口味。

#!/usr/bin/env perl

use strict;
use warnings;

package PACKAGE1;

use parent 'Exporter';
our @EXPORT = qw(Method1);

sub Method1 {
  print "PACKAGE1_Method1 \n";
}

package PACKAGE2;

use parent 'Exporter';
our @EXPORT = qw(Method1);

sub Method1 {
  print "PACKAGE2_Method1 \n";
}

package Includes;

use Import::Into;

# uncomment in mulitple files
#use PACKAGE1 ();  # prevent import
#use PACKAGE2 ();  # ditto

sub import {
  my $class = shift;
  my $caller = caller;

  PACKAGE1->import::into( $caller );
  PACKAGE2->import::into( $caller );
}

package Test1;
Includes->import; # in seperate files replace with `use Includes;`

Method1();

package Test2;
Includes->import; # ditto

Method1();

一个真实的例子是一个模块utf8::all,它广泛使用这种机制将大量的 unicode 内容加载到调用程序包中。

编辑

为了允许从模块中导入特定的东西Includes,你可以让它继承自Exporter并制作它@EXPORT@EXPORT_OK按照你的意思做。否则,您可以继续Import::Into制作捆绑包之类的东西。

sub import {
  my $class  = shift;
  my $bundle = shift;

  my $caller = caller;

  if ($bundle eq 'Bundle1') {
    PACKAGE1->import::into( $caller );
    ... # other things in Bundle1
  } elsif ($bundle eq 'Bundle2') {
    PACKAGE2->import::into( $caller );
    ... # other things in Bundle2
  }
}

然后在您的测试模块中

use Includes 'Bundle1';

总之,制作自己的import方法并不难,每一点都是神奇的Exporter。一旦您了解了符号表操作,您就不需要它,或者Import::Into,尽管它是一个稍微高级一点的主题。这是我在 Perl 时代早些时候问过的一个问题:Demystifying the Perl glob (*)

综上所述,如果继承和多态的面向对象概念可以完成这项工作,您可能也想研究该路线。这是一个例子:

#!/usr/bin/env perl

use strict;
use warnings;

package PACKAGE1;

sub Method1 {
  my $class = shift;
  print "PACKAGE1_Method1 \n";
}

sub Method2 {
  my $class = shift;
  print "PACKAGE1_Method2 \n";
}

package PACKAGE2;

# if multiple files use this
#use parent 'PACKAGE1';
# rather than
our @ISA = 'PACKAGE1';

# any methods in PACKAGE2 will override those declared in PACKAGE1 

sub Method1 {
  my $class = shift;
  print "PACKAGE2_Method1 \n";
}

package Test1;

# in seperate files need to use
#use PACKAGE2;

PACKAGE2->Method1();
PACKAGE2->Method2();

package Test2;

# ditto
#use PACKAGE1
#use PACKAGE2

PACKAGE2->Method1();
PACKAGE2->Method2();

# you can still use PACKAGE1 and get the originals
PACKAGE1->Method1();
PACKAGE1->Method2();

现在看到没有Includes包,也没有符号被导入Test*命名空间。PACKAGE2提供Method2,因为它继承自PACKAGE1并且它不会用自己的方法声明覆盖方法声明。

于 2013-02-02T06:01:16.543 回答