【问题标题】:Why does a function name have to be specified in a use statement?为什么必须在 use 语句中指定函数名?
【发布时间】:2018-09-20 02:51:08
【问题描述】:

perl中,有时需要在use语句中指定函数名。

例如:

use Data::DPath ('dpath');

会起作用,但是

use Data::DPath;

不会。

其他模块不需要指定函数名,例如:

use WWW::Mechanize;

为什么?

【问题讨论】:

  • 您是否有一个特定的WWW::Mechanize 示例,其中使用了一个函数而不包括它的名称?可以发MCVE吗?
  • @ray use WWW::Mechanize; my $mech = new WWW::Mechanize; $mech->get("http://www.google.com");
  • $mech->get(...) 只是一个调用实例方法的对象,所以并不是一回事。
  • @ray:你给出的建议很糟糕。 MCVE 与这个问题并不真正相关,如果想询问它,OP 自己的 Funcs 模块的问题需要是一个单独的问题。
  • @Borodin,我不太确定,特别是考虑到他在实际函数、带有方法的对象之间明显混淆,以及他评论说他有一个 Funcs.pm 模块,他可以导入并使用所有内容,而不在其中定义EXPORT/EXPORT_OK。有什么不对劲,这将有助于查看他使用的代码实际上导致他提出问题。我认为这不必是一个单独的问题,因为这与澄清他的情况有关。

标签: function perl perl-module


【解决方案1】:

每个模块都选择默认导出的函数。有些选择默认导出 no 函数,你必须要求它们。这样做有几个很好的理由,也有一个不好的理由。

如果你是WWW::Mechanize 这样的类,那么你不需要导出任何函数。一切都是类或对象方法。 my $mech = WWW::Mechanize->new.

如果你是像 strict 这样的编译指示,那么就没有函数或方法,它只需通过加载就可以工作。

默认情况下,某些模块导出的功能太多。一个例子是Test::Deep,它导出...

所有任何数组 array_each arrayelementsonly arraylength arraylengthonly bag 祝福 bool cmp_bag cmp_deeply cmp_methods cmp_set 代码 eq_deeply hash hash_each hashkeys hashkeysonly 忽略 Isa isa listmethods methods noclass none noneof num obj_isa re reftype regexpmatches regexponly regexpref regexprefonly scalarrefonly scalref 设置浅 str subbagof subhashof superbagof superhashof supersetof useclass的子集

当另一个模块尝试导出相同的函数时,或者如果您编写具有相同名称的函数时,就会出现问题。然后它们发生冲突,你会收到神秘的警告。

$ cat ~/tmp/test.plx
use Test::Deep;
use List::Util qw(all);

$ perl -w ~/tmp/test.plx
Subroutine main::all redefined at /Users/schwern/perl5/perlbrew/perls/perl-5.20.2/lib/5.20.2/Exporter.pm line 66.
 at /Users/schwern/tmp/test.plx line 2.
Prototype mismatch: sub main::all: none vs (&@) at /Users/schwern/perl5/perlbrew/perls/perl-5.20.2/lib/5.20.2/Exporter.pm line 66.
 at /Users/schwern/tmp/test.plx line 2.

因此,不鼓励导出大量函数。比如the Exporter documentation advises...

不要导出方法名!

不要在没有充分理由的情况下默认导出任何其他内容!

导出污染模块用户的命名空间。如果您必须导出,请尝试使用@EXPORT_OK 而不是@EXPORT,并避免使用简短或常见的符号名称以减少名称冲突的风险。

不幸的是,有些模块做得太过分了。 Data::DPath 就是一个很好的例子。它有一个非常清晰的主函数 dpath(),它应该默认导出。否则基本没用。

您可以随时使用use Some::Module (); 关闭导出功能。

【讨论】:

  • 我有自己的 Funcs.pm 文件,其中包含函数。为了导入函数,我所要做的就是use Funcs; Funcs.pm 文件只包含函数,没有提到@EXPORT_OK 的任何地方。
  • @CJ7 我想你没有在 Funcs.pm 中放一个包名,所以它都进入了默认包main。这是非常不礼貌的。 Funcs.pm 将使用 Funcs 包并且不会无缘无故污染其他包,这是一个非常强烈的约定
  • @CJ7:可能你的文件中没有包符号,所以整个东西在全局命名空间中自动可用。你可以这样做——它有效。但是正式的模块(例如WWW::Mechanize)使用包符号来避免暴露它们的内部,并且训练有素的模块只导出最少的必要名称,或用户指定的名称列表。它避免了对命名空间的不必要污染。
【解决方案2】:

原因是某些模块中只包含函数,它们可能会或可能不会选择默认导出它们,这意味着它们可能需要由脚本显式导入才能直接访问或使用完全限定名称访问它们。例如:

# in some script
use SomeModule;
# ...
SomeModule::some_function(...);

use SomeModule ('some_function');
# ...
some_function(...);

如果模块不打算以面向对象的方式使用,即没有定义类并且 my $obj = SomeModule->new() 等行不起作用,则可能会出现这种情况。

如果模块在EXPORT_OK 数组中定义了内容,这意味着客户端代码只有在“请求”时才能访问它,而不是在它实际存在于EXPORT 时“自动”访问它数组。

某些模块通过@EXPORT 数组自动导出其内容。 This questionExporter docs 对此有更多详细信息。

如果您没有实际发布MCVE,很难知道您在Funcs.pm 模块中做了什么,这可能允许您在不使用EXPORTEXPORT_OK 数组的情况下导入所有内容。正如@JonathanLeffler 在 cmets 中建议的那样,也许您没有在模块中包含 package Funcs; 行。也许你做了别的事情。 Perl 是人们以 TMTOWTDI 的口头禅为荣的语言之一,这通常会达到有害/适得其反的程度,恕我直言。

您提供的第二个示例非常不同且相当简单。当你有类似的东西时:

use WWW::Mechanize;
my $mech = new WWW::Mechanize;
$mech->get("http://www.google.com");

您只是在实例化一个WWW::Mechanize 类型的对象并在其上调用一个名为get 的实例方法。无需导入对象的方法,因为这些方法是对象本身的一部分。寻求 OOP 方法的模块并不意味着导出任何东西。它们是不同的情况。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-12-14
    • 1970-01-01
    • 2020-05-23
    • 2013-10-02
    • 2019-02-19
    • 1970-01-01
    • 2014-05-26
    • 1970-01-01
    相关资源
    最近更新 更多