【问题标题】:Subroutines defined in required file assigned to wrong namespace after use of custom module使用自定义模块后,在所需文件中定义的子例程分配给错误的命名空间
【发布时间】:2015-08-04 21:26:54
【问题描述】:

在我正在编写的脚本中,我有类似以下内容:

require "environment.pm"; # Various definitions, including subroutine get_coll()
use Custom::Module;

Custom::Module文件中,我以

开头
package Custom::Module;

并以

结尾
1;

当我尝试拨打 get_coll() 时,我收到一个错误:

( main::get_coll() ) 未定义。

如果我需要 Custom::Module 而不是使用或更改调用是

Custom::Module::get_coll();

它工作正常。这使我相信CustomModule 的“使用”语句正在更改“活动”命名空间,因此当处理get_coll() 时(因为它是在调用时处理的)它被分配给该命名空间而不是主命名空间.这似乎不是常规 perl 模块的问题,但从我浏览过的模块来看,我没有注意到任何不同之处会导致命名空间在模块加载后“恢复”回 main。任何有助于更好地理解命名空间使用或修复模块以免导致此问题的帮助将不胜感激。

旁注:对我来说,仅仅“要求”该模块并不是一个大问题,但这对我来说是出乎意料的行为,所以我主要只是想更好地理解为什么会发生这种情况。

【问题讨论】:

  • package 这样做的,而不是use。这就是package 的全部意义所在。
  • 我认为你错了。如果你仔细测试你会发现,如果use Custom::Module不允许你直接调用get_coll,那么require Custom::Module也不能。如果您使用Exporter use Custom::Module 正确实现Custom::Module,那么它将将该符号导入调用代码的命名空间。否则,您必须将其完全限定为 Custom::Module::get_coll

标签: perl module namespaces


【解决方案1】:

我已尝试完全按照您的描述重新创建文件。这是我的三个源文件

你说

在我正在编写的脚本中,我有类似以下内容:

require "environment.pm"; # Various definitions, including subroutine get_coll()
use Custom::Module;

当我尝试调用 get_coll() 时出现错误

虽然你没有说你在哪里调用get_coll,但我猜它在主脚本中,我认为这相当于这个脚本文件

my_script.pl

use strict;
use warnings;
use 5.010;

require 'environment.pm';

use Custom::Module;

say get_coll();

还有这个模块文件

environment.pm

use strict;
use warnings;
use 5.010;

sub get_coll {
  return 'coll';
}

1;

然后你说

Custom::Module 文件中,我从

package Custom::Module;

并以

结尾
1;

所以我写了这个模块文件。它没有任何内容,因为你没有描述任何内容

Custom/Module.pm

use strict;
use warnings;
use 5.010;

package Custom::Module;

1;

现在,当我运行 my_script.pl 时,我得到了

coll

没有警告或错误消息,这完全符合我的预期

我担心你说environment.pm 包含get_coll,但你可以调用Custom::Module::get_collCustom/Module.pm 也有require 'environment.pm' 吗?

如果您能指出我在哪里误解了您的描述,请指出,因为我目前无法复制您的问题,因此无法为您提供帮助

否则,我建议您使用这三个文件来创建您的问题的Short, Self-Contained, Correct Example。这将极大地帮助我们为您找到解决方案

【讨论】:

  • 啊...我认为您正在做某事。在 Custom::Module 包中也需要带有 get_coll 子例程的文件。既然它正在被使用,那么它将在那个命名空间中加载那个子例程!这么简单的错误。感谢您的帮助和解释!
  • @Fincher:不客气。一般来说,您应该坚持使用use 而不是require,除非您有充分的理由不这样做
【解决方案2】:

您误认为requireuse 在命名空间子例程的定义方面有任何不同。事实并非如此。而且您误认为use 会影响当前软件包。它没有。

# Custom/Module.pm
package Custom::Module;
# the namespace is now "Custom::Module"
sub foo { ... }  # defines  &Custom::Module::foo
sub bar { ... }  # defines  &Custom::Module::bar
1;   # end of Custom/Module.pm

---

# mainScript.pl
# without an explicit 'package' statement, we are in namespace "main"
use Custom::Module;    # parses Custom/Module.pm
# but when the 'use' statement is complete, we are still in namespace "main"
sub baz { ... }   # defines  &main::baz
$x = baz();       # calls &main::baz
$y = foo();       # calls &main::foo, error if main::foo not defined
$z = Custom::Module::bar();  # calls &Custom::Module::bar
...

cmets 描述了我们在每个文件的每个部分中的名称空间。如果我们说require Custom::Module 而不是use Custom::Module,这一切都没有什么不同。

现在,当没有main::bar() 并且您将指代哪个bar 子例程没有歧义时,一直输入Custom::Module::bar() 可能会很麻烦。让 perl 将您对 bar 的调用识别为引用 Custom::Module::bar 的关键是将子例程引用从 Custom::Module 复制到您当前的命名空间中。也就是说,使main::bar() 引用与Custom::Module::bar() 相同的子例程。

执行此操作的规范方法是使用 Exporter 模块。执行此操作的低级方法(以及 Exporter 在幕后所做的)是操作 perl 的符号表。

对于您的具体问题,您可以像这样使用Exporter

 # Custom/Module.pm
 package Custom::Module;
 use base 'Exporter';      # make Custom::Module inherit from Exporter
 our @EXPORT = qw(foo bar);
 sub foo { ... }
 sub bar { ... }
 ...
 1;

现在任何其他调用use Custom::Module 的文件都会将函数get_coll 导入其命名空间。这通过操纵符号表在Exporter 的幕后完成。具体来说,从包main 调用use Custom::Module 将得到Exporter 来进行类似的typeglob 赋值

*main::foo = *Custom::Module::foo;

这将使您从main::foo() 调用的函数调用在Custom::Module::foo() 中定义的代码。

【讨论】:

  • 最近,人们认识到从Exporter导入子类化更好。所以use base 'Exporter' 应该是use Exporter 'import'。此外,*main::foo = *Custom::Module::foo 复制整个 foo glob,并且应该是 *main::foo = \&Custom::Module::foo
  • 除非你说@EXPORT = qw(&foo),否则Exporter 也会复制整个foo glob。
  • 不,不会。请参阅How to Export in the Exporter documentation,其中说“符号必须按全名给出,但函数前面的& 符号是可选的”。我承认这对于可能导出的其他内容并不确定,但经验事实是use Module 'foo' &Module::foo 而不是@Module::foo$Module::foo 等。
  • 我理解您的评论,但不认为它解决了我不理解的主要问题。
  • 我在 file1.pm 中有子程序 get_coll()。我在包含 Custom::Package 包的文件中有其他子例程。
    如果我只需要“file1.pm”,我可以在 main.pl 中调用 get_coll() 并且它可以工作。如果我添加“使用 Custom::Package”,则 get_coll() 不再位于主命名空间中。如果我将调用更改为 Custom::Package::get_coll(),我可以调用该函数。如果我将“使用 Custom::Package”更改为“需要 Custom::Package”,“get_coll()”将再次起作用。似乎很明显是一个命名空间问题,不是吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-30
  • 1970-01-01
  • 2017-06-11
相关资源
最近更新 更多