这是一个有趣的Perl行为。 (至少对我:))
我有两个包PACKAGE1
和PACKAGE2
,它们导出具有相同名称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。有什么办法吗?
答案 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
,并且不会使用自己的方法声明覆盖方法声明。