Perl:同一个文件中的两个包不能导入同一个包吗?

时间:2013-02-02 05:22:16

标签: perl perl-module

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

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

由于会有这么多包将导出同样的函数,use - Perl文件中的所有内容都会很乏味。所以,我创建了一个包含INCLUDES.pm s的常规包含文件use

INCLUDES.pm:

use PACKAGE1;
use PACKAGE2;

1;

PACKAGE1.pm:

package PACKAGE1;

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

sub Method1{
print "PACKAGE1_Method1 \n";
}

1;

PACKAGE2.pm:

package PACKAGE2;

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

sub Method1{
    print "PACKAGE2_Method1 \n";
}

1;

Tests.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();

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

输出令我惊讶。 我希望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模块中的方法将具有相同的名称。但是只应使用High preference perl模块中的方法。

例如,如果PACKAGE1包含Method1(), Method2()& PACKAGE2仅包含Method1()Method1()应使用PACKAGE2& Method2() PACKAGE1应使用{{1}}

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

2 个答案:

答案 0 :(得分:4)

在Perl中,use Module相当于

BEGIN { require Module; Module->import; }

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

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

答案 1 :(得分: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,并且不会使用自己的方法声明覆盖方法声明。