Perl - 子例程重新定义

时间:2010-08-06 22:33:19

标签: perl subroutine

我之前已经问过这个问题或者搜过其他人问过 - 为什么我会收到警告“子程序mySub重新定义在../lib/Common.pm第x行”?并且你总是得到答案你在相同的代码中声明了两次。我创建了这个测试包:

整个文件---------------

package MyCommonPkg;

use strict;

sub thisSubroutineIsNotDefinedAnywhereElse{
}

1;

整个文件---------------

我从perl脚本使用这个包,它使用其他包也使用这个包,我收到警告:

子程序ThisSubroutineIsNotDefinedAnywhereElse在../lib/MyCommonPkg.pm第19行重新定义。

我保证我没有在其他任何地方声明这个子。这是由循环引用引起的吗?如何跟踪和修复此警告的原因?

9 个答案:

答案 0 :(得分:34)

你有依赖循环吗?如果Perl开始编译脚本并遇到如下所示的行:

use PackageA;

Perl暂停编写脚本;找到PackageA.pm并开始编译它。如果它遇到这样的一行:

use PackageB;

Perl暂停编译PackageA;找到PackageB.pm并开始编译它。通常情况下,这将成功完成,Perl将返回完成编译PackageA,当成功完成时,它将返回编译脚本,当成功完成时,它将开始执行已编译的操作码。

然而,如果PackageB.pm包含以下行:

use PackageA;

你可能会认为它不会做任何事情,因为Perl已经处理过PackageA.pm但问题是它还没有完成。所以Perl将暂停PackageB的编译并从头开始再次开始编译PackageA.pm。这可能会触发您重新定义PackageA中子例程的消息。

作为一般规则,两个包不应相互依赖。但有时循环更难定位,因为它是由第三个包引起的。

答案 1 :(得分:24)

如果在不同的包中有两个具有相同名称的子例程,则应该看到此警告(启用警告时)为“Subroutine new redefined ....”。 一个简单的原因(非常接近Grant McLean所说的,但仍然不完全正确)是你必须让你的包跳过编译阶段然后再需要。这样,Perl命名空间管理器在编译时将找不到任何具有相同名称的冲突符号,如果模块没有任何错误,那么它们之后也会正常工作。

请确保您实施

需要模块;

声明而不是

使用模块;

你不应该再看到这个警告。

答案 2 :(得分:7)

如果您使用的是具有不区分大小写的文件系统(Windows,通常是OSX)的系统,并且您在一个文件中执行use Common而在另一个文件中执行use common,则可能会导致类似问题此

答案 3 :(得分:4)

这听起来像是由循环依赖引起的问题。以下是如何追踪它。如果你的问题类看起来像这样:

package AlientPlanet;
use Dinosaurs;
sub has_dinosaurs {...}
1;

然后将您的示例更改为:

package AlienPlanet;
sub has_dinosaurs {...}     # <-- swap
use Dinosaurs;              # <-- swap
1;

现在使用Carp::Always编译代码,如下所示:

⚡ perl -MCarp::Always -c lib/AlienPlanet.pm                                                                                                            
Subroutine has_dinosaurs redefined at lib/AlienPlanet.pm line 4.
    require AlienPlanet.pm called at lib/Dinosaurs.pm line 4
    Dinosaurs::BEGIN() called at lib/AlienPlanet.pm line 4
    eval {...} called at lib/AlienPlanet.pm line 4
    require Dinosaurs.pm called at lib/AlienPlanet.pm line 5
    AlienPlanet::BEGIN() called at lib/AlienPlanet.pm line 4
    eval {...} called at lib/AlienPlanet.pm line 4
lib/AlienPlanet.pm syntax OK

现在你有了一个堆栈跟踪,你可以看到循环的位置。快速而肮脏的解决方案是在Dinosaurs.pm。

中使用Class::Load

有关更详细的说明,请尝试我的blog post

答案 4 :(得分:2)

您是否有机会在网络服务器上运行此cgi脚本?

我发现我需要重新启动网络服务器以解决此警告。

答案 5 :(得分:1)

查看计划package MyCommonPkg.pm并查看其内容。它有这样的东西吗?

package MyCommonPkg;

use Exporter qw(import);   #  Might be "require" and not "use"
our @EXPORT = qw(thisSubroutineIsNotDefinedAnywhereElse);

语法可能略有不同。您应该看到的主要内容是package语句,它使用Exporter并且@EXPORT数组中包含您的子例程的名称。

发生了什么是命名空间冲突。您的包正在定义您正在定义的相同子例程。

为了防止这种情况发生,Perl使用名称空间。默认情况下,您的命名空间为main。但是,假设包使用package命令定义它们各自的名称。

子例程或变量的完整命名空间是命名空间,后跟双冒号,后跟子例程或变量名。例如,在您查看File::Find时,您会看到对变量$File::Find::name$File::Find::dir的引用。这些是$name命名空间下$dir包内的变量File/Find.pmFile::Find

为了使您的工作更轻松,软件包可以导出它们的命名空间。例如,如果我使用File::Copy,O可以这样做:

...
use File::Copy
...
copy ($file, $to_dir);

而不是:

...
use File::Copy
...
File::Copy::copy ($file, $to_dir);

如果查看File/Copy.pm,您会看到以下内容:

package File::Copy;
...
our(@ISA, @EXPORT, @EXPORT_OK, $VERSION, $Too_Big, $Syscopy_is_copy);
...
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(copy move);

package File::Copy;定义命名空间。 require Exporter;@ISA = qw(Exporter)允许包将子例程和变量导出到 main 命名空间。 @EXPORT会自动将copymove子程序导入名称空间 ,无论您是否需要它,不!

最后一点非常重要。现在,使用@EXPORT认为不礼仪。相反,您应该使用@EXPORT_OK,它要求您列出要使用的子例程。像Scalar::Util这样的更现代的软件包可以做到这一点。

所以有几件事。首先,您的MyCommonPkg是否有package MyCommonPkg;声明。如果没有,它应该。这使包子程序和变量不会以令人讨厌的方式影响您的程序。然后,您可以使用@EXPORT@EXPORT_OK

如果MyCommonPkg确实有package声明,是否使用@EXPORT?如果是这样,您有几种方法可以避免此问题:

  • 忽略警告。这只是一个警告。既然您知道要重新定义子例程,并且想要使用子例程的定义,请忽略它。

您可以在重新定义子例程时关闭警告:

use MyCommonPkg;

no warnings qw(redefine);
sub thisSubroutineIsNotDefinedAnywhereElse {
   ...
}
use warnings qw(redefine);
  • 使用require MyCommonPkg;代替use MyCommonPkg;。这将阻止将任何子例程或变量导入您的命名空间,包括您想要使用的子例程或变量。假设MyCommonPkg定义了四个子例程:thisSubroutineIsNotDefinedAnywhereElsefoobarbarfoo。要使用这些子程序中的任何一个。

你需要这样做:

my $answer = MyCommonPkg::foo( $input );

不好玩。

  • 为子例程使用其他名称。应记录此子例程在MyCommonPkg中定义,如果要使用MyCommonPkg,则不应使用导出的子例程名称。

  • 最后,如果MyCommonPkg相当新,并且未在数十个程序中使用,请使用@EXPORT_OK代替@EXPORT,并确保所有使用的程序修改MyCommonPkg以导出他们想要的子例程:

像这样:

use MyCommonPkg qw(foo bar);

在这种情况下,仅导出子例程foobar。子例程thisSubroutineIsNotDefinedAnywhereElsebarfoo不会导出到您的环境中。

答案 6 :(得分:0)

我尝试使用“package Common.pm”作为包名。编译器给了我错误。非常好吗?您使用的是什么版本的Perl?我在5.10.0和5.12.1上尝试过它。

即使您可以编译,最好删除.pm文件。例如;

文件:some_package.pm;

package some_package;
use strict;

sub yadayadayada { ... }

1;

答案 7 :(得分:0)

确保您在模块末尾没有忘记这一行:

1;

我知道它已包含在这里的一些例子中,但我提到它是因为它很容易被忽视,而在我的情况下,它被证明是导致错误的唯一原因!

答案 8 :(得分:0)

我有同样的问题;这是因为程序使用了一个模块,子程序既存在于程序中,也存在于perl模块中;