Perl - 重新定义子例程
我之前曾问过这个问题,或者搜索过并看到其他人问过 - 为什么我收到警告“Subroutine mySub redefine at ../lib/Common.pm line x”?并且您总是会得到答案您在同一代码中声明了两次 sub。我创建了这个测试包:
整个文件 ---------------
package MyCommonPkg;
use strict;
sub thisSubroutineIsNotDefinedAnywhereElse{
}
1;
整个文件 ---------------
我从 perl 脚本中使用这个包,它使用其他包,也使用这个包,我收到警告:
Subroutine ThisSubroutineIsNotDefinedAnywhereElse redefine at ../lib/MyCommonPkg.pm line 19.
我保证我没有在其他地方声明这个子程序。那么这是由循环引用引起的吗?我该如何追踪此警告的原因并进行修复?
I have asked this question before or searched and seen others ask - why am I getting the warning "Subroutine mySub redefined at ../lib/Common.pm line x"? and you always get the answer you declared the sub twice in the same code. I created this test package:
ENTIRE FILE ---------------
package MyCommonPkg;
use strict;
sub thisSubroutineIsNotDefinedAnywhereElse{
}
1;
ENTIRE FILE ---------------
and I USE this package from a perl script, which uses other packages, that use this package also, and I get the warning:
Subroutine ThisSubroutineIsNotDefinedAnywhereElse redefined at ../lib/MyCommonPkg.pm line 19.
I promise I did not declare this sub anywhere else. So is this caused by a circular reference? How can I go about tracking the cause of this warning down and fixing?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
你有依赖循环吗?如果 Perl 开始编译你的脚本并遇到这样的行:
Perl 暂停编译你的脚本;找到 PackageA.pm 并开始编译它。如果遇到这样一行:
Perl 暂停 PackageA 的编译;找到 PackageB.pm 并开始编译它。通常情况下,这会成功完成,Perl 将返回完成编译 PackageA,当成功完成时,它将返回编译脚本,当成功完成时,它将开始执行编译后的操作码。
但是,如果 PackageB.pm 包含这一行:
您可能认为它不会执行任何操作,因为 Perl 已经处理了 PackageA.pm,但问题是它尚未完成。所以Perl会暂停PackageB的编译,并从头开始重新编译PackageA.pm。这可能会触发您看到的有关 PackageA 中的子例程被重新定义的消息。
作为一般规则,两个包不应同时相互依赖。然而,有时循环更难定位,因为它是由第三个包引起的。
Do you have a dependency loop? If Perl starts compiling your script and encounters a line like this:
Perl pauses the compilation of your script; locates PackageA.pm and starts compiling it. If it encounters a line like this:
Perl pauses the compilation of PackageA; locates PackageB.pm and starts compiling it. Normally, that would complete successfully, and Perl would go back to complete compiling PackageA and when that completes successfully it would go back to compiling your script and when that completes successfully it would start to execute the compiled opcodes.
However, if PackageB.pm contains this line:
You might expect it would do nothing since Perl has already processed PackageA.pm but the problem is that it hasn't finished yet. So Perl will pause the compilation of PackageB and start compiling PackageA.pm again from the beginning. That could trigger the message you're seeing about subroutines in PackageA being redefined.
As a general rule, two packages should not both depend on each other. Sometimes however the loop is harder to locate because it is caused by a third package.
当不同包中有两个同名的子例程时,您应该看到此警告(当启用警告时)为“子例程新重新定义......”。
简单的原因(这与 Grant McLean 所说的非常接近,但仍然不完全)是你必须让你的包跳过编译阶段并 make then require。这样,Perl 命名空间管理器在编译时就不会发现任何此类具有相同名称的冲突符号,并且如果您的模块没有任何错误,它们之后也会正常工作。
只需确保您实现
require 模块即可;
声明而不是
使用Module;
您不应再看到此警告。
When you have two subroutines with the same name in different packages, you ought to see this warning (when warnings are enabled) as "Subroutine new redefined....".
The simple reason (which is very close to what Grant McLean said, but still not exactly) is you must get your packages skip the compiling phase and make then require. This way, the Perl namespace manager will not find any such conflicting symbols with same name at compile time, and if you modules do not have any errors, they will work just fine afterwards also.
Just make sure you implement
require Module;
statement rather than
use Module;
You should not see this warning again.
如果您使用的系统具有不区分大小写的文件系统(Windows,通常是 OSX),并且您在一个文件中
使用 Common
,并在另一个文件中使用 common
,你可能会导致这样的问题。If you're on a system with a case-insensitive filesystem (Windows, and quite often OSX), and you do
use Common
in one file anduse common
in another, you can cause problems like this.这听起来像是循环依赖引起的问题。以下是如何追踪它。如果您的问题类如下所示:
然后将示例更改为如下所示:
现在使用 Carp 编译代码::Always 像这样:
现在您有了堆栈跟踪,您可以看到循环在哪里。快速但肮脏的解决方案是在 Dinosaurs.pm 中使用 Class::Load 。
如需更详细的解释,请尝试我的博客文章。
This sounds like a problem caused by circular dependencies. Here is how to track it down. If your problem class looks like this:
Then change your example to look like this:
Now compile your code with Carp::Always like this:
Now that you have a stacktrace you can see where the loop is. The quick and dirty solution is to use Class::Load in Dinosaurs.pm.
For a more detailed explanation try my blog post.
您是否有机会在网络服务器上将其作为 cgi 脚本运行?
我发现我需要重新启动网络服务器才能解决此警告。
Are you by any chance running this as a cgi-script on a web-server?
I find I need to restart the webserver to get around this warning.
看一下程序
package MyCommonPkg.pm
并看看它说了什么。有这样的东西吗?语法可能有点不同。您应该看到的主要内容是
package
语句,它使用Exporter
并且@EXPORT
数组中包含您的子例程的名称。发生的事情是命名空间冲突。您的包定义的子例程与您定义的子例程相同。
为了防止这种情况发生,Perl 使用命名空间。默认情况下,您的命名空间是
main
。但是,包应该使用package
命令定义它们自己的单独同名对象。子例程或变量的完整命名空间是命名空间后跟双冒号,然后是子例程或变量名称。例如,如果您查看 File::Find,您将看到对变量的引用
$File::Find::name
和$File::Find::dir
。这些是File::Find
File/Find.pm 包内的变量$name
和$dir
> 命名空间。为了让您更轻松,包可以将其变量和子例程导出到您的main命名空间中。例如,如果我使用 File::Copy,O 可以这样做:
而不是:
如果您查看
File/Copy.pm
,您将看到以下内容:包 File::Copy;
定义了一个命名空间。require Exporter;
和@ISA = qw(Exporter)
允许包将子例程和变量导出到 main 命名空间中。@EXPORT
会自动将copy
和move
子例程导入到 main 命名空间中,而无需告诉您任何信息。 strong>无论您是否想要它们!最后一点非常重要。现在使用
@EXPORT
被认为是不礼貌。相反,您应该使用@EXPORT_OK
,它要求您列出要使用的子例程。更现代的软件包如 Scalar::Util 可以做到这一点。有几件事。首先,您的
MyCommonPkg
是否有package MyCommonPkg;
语句。如果没有,应该如此。这可以防止包子例程和变量以令人讨厌的方式影响您的程序。然后,您可以使用@EXPORT
或@EXPORT_OK
。如果
MyCommonPkg
确实有package
语句,它是否使用@EXPORT
?如果是这样,您可以通过多种方法避免此问题:您可以执行此操作以在重新定义子例程时关闭警告:
require MyCommonPkg;
而不是use MyCommonPkg;
。这将阻止将任何子例程或变量导入您的命名空间,包括您想要使用的子例程或变量。假设MyCommonPkg
定义了四个子例程:thisSubroutineIsNotDefinedAnywhereElse
、foo
、bar
和barfoo
。使用这些子例程中的任何一个。你需要这样做:
不好玩。
为您的子例程使用另一个名称。应该已经记录了该子例程是在
MyCommonPkg
中定义的,如果您想使用MyCommonPkg
,则不应使用导出的子例程名称。最后,如果
MyCommonPkg
相当新,并且未在数十个程序中使用,请使用@EXPORT_OK
而不是@EXPORT
,并确保所有使用MyCommonPkg
的程序都被修改为导出它们想要的子例程:像这样:
在这种情况下,仅导出子例程
foo
和bar
。子例程thisSubroutineIsNotDefinedAnywhereElse
和barfoo
不会导出到您的环境中。Take a look at the program
package MyCommonPkg.pm
and see what it says. Does it have something like this?The syntax may be a bit different. The main things you should see are the
package
statement, that it's usingExporter
and that the@EXPORT
array has your subroutine's name in it.What is going on is a namespace clash. Your package is defining the same subroutine you're defining.
In order to prevent this from happening, Perl uses namespaces. By default, your namespace is
main
. However, packages are suppose to define their own separate namesakes using thepackage
command.The full namespace of a subroutine or variable is the namespace followed by a double colon, followed by the subroutine or variable name. For example, of you look at File::Find, you will see references to the variables
$File::Find::name
and$File::Find::dir
. These are the variables$name
and$dir
inside theFile/Find.pm
package under theFile::Find
namespace.In order to make things easier for you, packages can export their variables and subroutines into your main namespace. For example, if I use File::Copy, O can do this:
Instead of:
If you look at
File/Copy.pm
, you will see the following:The
package File::Copy;
defines a namespace. Therequire Exporter;
and the@ISA = qw(Exporter)
allows the package to export subroutines and variables into the main namespace. The@EXPORT
automatically, without it telling you anything, imports thecopy
andmove
subroutines into the main namespace whether you want them or not!That last bit is very important. It is now considered bad manners to use
@EXPORT
. Instead, you should use@EXPORT_OK
which requires you to list the subroutines you want to use. More modern packages like Scalar::Util do this.So several things. First, does your
MyCommonPkg
have apackage MyCommonPkg;
statement. If not, it should. This keeps the packages subroutines and variables from affecting your program in nasty ways. Then, you can use@EXPORT
or@EXPORT_OK
.If
MyCommonPkg
does have apackage
statement, does it use@EXPORT
? If so, you have several ways you can avoid this issue:You can do this to turn off the warning as you redefine the subroutine:
require MyCommonPkg;
instead ofuse MyCommonPkg;
. This will prevent the importing of any subroutines or variables into your namespace including ones you wanted to use. Let's sayMyCommonPkg
defines four subroutines:thisSubroutineIsNotDefinedAnywhereElse
,foo
,bar
, andbarfoo
. To use any of these subroutines.You need to do this:
Not fun.
Use another name for your subroutine. It should have been documented that this subroutine is defined in
MyCommonPkg
, and if you want to useMyCommonPkg
, you shouldn't use subroutine names that are exported.Finally, if
MyCommonPkg
is fairly new, and isn't used in dozens of programs, use@EXPORT_OK
instead of@EXPORT
, and make sure all the programs that useMyCommonPkg
are modified to export the subroutines they want:Like this:
In this case, only subroutines
foo
andbar
are exported. The subroutinesthisSubroutineIsNotDefinedAnywhereElse
andbarfoo
are not exported into your environment.我尝试使用“package Common.pm”作为包名称。编译器给了我错误。非常友善吧?你使用什么版本的 Perl?我在5.10.0和5.12.1上尝试过。
即使您可以编译,删除 .pm 文件也是一个好习惯。例如;
文件:some_package.pm;
I tried to use "package Common.pm" as a package name. The compiler gave me errors. Very kind of it eh? What version of Perl are you using? I tried it on 5.10.0 and 5.12.1.
Even if you can compile it is good practice to remove the .pm file. For Example;
File: some_package.pm;
确保您没有忘记模块末尾的这一行:
1;
我知道它已包含在此处的一些示例中,但我提到它是因为它很容易被忽视,并且就我而言,这是错误的唯一原因!
Make sure you didn't forget this line at the end of your module:
1;
I know it's been included in a few of the examples here, but I mention it because it is easy to overlook, and in my case it turned out to be the sole cause of the errors!
我也遇到了同样的问题;这是因为程序使用了模块,并且子程序同时存在于程序和perl模块中;
I had the same problem; It was because the program used a module and the subroutine was present both in the program and in the perl module;