跑过这个小小的金块......我想我已经弄清楚了,但看看是否有人可以放下更多的光。
我的PERL5LIB
环境变量中有一个用于记录的模块。它位于e:/Scripts/Log/Logger.pm
。以下是摘录:
package Log::Logger;
...
package Log::NullLogger;
use base 'Log::Logger';
...
package Log::FileLogger;
use base 'Log::Logger';
...
package Log::DateFileLogger;
use base 'Log::FileLogger';
...
package LogTester;
use Data::Dump 'dump';
test() unless caller();
sub test {
warn dump \%INC;
...
}
我想对此模块进行一些更改,因此我将其复制到e:/Test/Log/Log/Logger.pm
并开始进行更改和测试...我添加了一个新类并更改了Log::Logger
包中的继承例程。 ..
package BasicFormatter;
...
但我的测试很奇怪。一些新代码正在使用,有些则没有。 %INC
的转储有:
"Log/Logger.pm" => "e:/Scripts/Log/Logger.pm"
... 旧模块!据我所知,use base 'Log::Logger'
从Log/Logger.pm
出去并重新加载PERL5LIB
,只替换其中的那些包(即package BasicFormatter
来自新模块,但是所有其他包都来自旧模块。)
我读到了use parent
,当我用use base
替换use parent
时,use parent 'Log::FileLogger'
上的编译错误表面上是因为没有Log/FileLogger.pm
模块。 use base
从未抱怨过这一点 - 我认为use parent
存在弱点。
我添加了
use lib '..';
到模块的顶部,一切都与use base
一起工作,但这不是我喜欢的修复,因为当我发布"时它不会出现在最后一个模块中。它回到e:/Scripts/Log/Logger.pm
。使用use lib '..'
,%INC
现在显示
"Log/Logger.pm" => "../Log/Logger.pm"
这是有道理的。
我的下一步是删除use lib '..'
并将所有use base...
替换为our @ISA=(...)
。当我这样做时,代码开始工作,%INC
根本没有显示Log/Logger.pm
的条目。
注意,当我在e:/Test/Log/TestLogger.pl
中使用
use lib '.';
在其中,它会提起e:/Test/Log/Log/Logger.pm
罚款,而use base
来电则不会重新要求该套餐。
所以,我的结论就是这样......当模块作为modulino运行时,模块不会被加载"就Perl而言。因此,use base
对该模块的第一次尝试会执行require <module>
,然后从@INC
路径加载它,这可能会替换正在执行的modulino中的包。使用@ISA
会绕过对require
的调用,因此模块不会从@INC
加载。
其他人得到了更多关于此的信息......正在测试一个模块作为modulino只是一个坏主意?
答案 0 :(得分:5)
我认为澄清一些事情是有用的:
@ISA
用于定义包的父类。它不会影响包的加载方式。您可以直接操作它,也可以使用parent
或略长的base
pragma。直接操纵@ISA
和这两个pragma之间的区别在于pragma执行的更多:use base qw/Foo Bar/;
和use parent qw/Foo Bar/;
在效果上大致相似
BEGIN {
require Foo;
require Bar;
push @ISA, qw(Foo Bar);
}
意味着他们不仅会操纵@ISA
,还会使用require
加载包。有一个例外,即use parent -norequire, qw/Foo Bar/;
- 如果你似乎在展示所有的软件包都驻留在同一个文件中,这可能很有用。但是,两个pragma都不会影响require
的操作方式,包括它的外观,因为您需要操作@INC
。
@INC
是Perl的use
,require
和do
查找文件的地方(后者有一些例外)。有几种方法可以操纵@INC
:
perl -I/path/to/modules script.pl
(请参阅perlrun)use lib '/path/to/modules';
,或直接操纵@INC
PERL5LIB
环境变量sitecustomize.pl
(仅限于configured) Perl按顺序查看@INC
。因此,在您的情况下,如果Perl加载了错误版本的Log::Logger
,这意味着Perl会在@INC
中找到它,而不是您实际想要加载的版本。我建议检查@INC
并确保只包含您实际想要加载模块的路径,或者放置&#34;开发的路径。 Log::Logger
中较早的@INC
版本。例如,当我开发模块时,我通常在命令行上指定开发版本的目录,例如: perl -Ilib ...
。
请注意,Perl 5.26发生了变化:the current directory .
was removed as a default entry in @INC
。 (Perl 5.24.1 also introduced一些相关的变化。)
%INC
基本上只是跟踪已经加载了哪些模块,因此require
和use
不会加载相同的模块两次。虽然可以通过手动将其条目添加到require
来欺骗%INC
而不查找文件,但通常有更好的解决方案。
答案 1 :(得分:5)
parent
要求您明确说明您是希望它实际加载父模块还是仅设置继承。这是一种力量,而不是弱点; base
总是尝试加载模块的弱点,导致您遇到的问题。
执行:
use parent -norequire => 'Log::Logger';
...
use parent -norequire => 'Log::FileLogger';
等
也就是说,如果您始终使用base
或use
加载模块,则问题require
不会发生;如果您只是运行一个模块(例如perl Foo/Bar.pm
)或以其他方式加载它(use Test::Log::Logger
而不是use lib 'Test'; use Log::Logger
??)并且您的代码也尝试use
它,那么您总是冒险找到了多个版本。
答案 2 :(得分:2)
我建议你将每个模块放在一个单独的文件中。要内联模块以便您可以使用use Module
,您可以通过替换
use Log::Logger;
与
BEGIN {
package Log::Logger;
...
$INC{"Log/Logger.pm"} = 1;
}
BEGIN { import Log::Logger; }
然后,use Log::Logger;
(包括use base 'Log::Logger';
和use parent 'Log::Logger';
)的未来使用将正常运行,包括导出。
在这种特殊情况下,@ ysth的解决方案就足够了。无论如何,我建议远离base.pm。