【问题标题】:Why would you want to export symbols in Perl?为什么要在 Perl 中导出符号?
【发布时间】:2009-12-13 02:05:41
【问题描述】:

Perl 允许一个包将符号导出到另一个包的命名空间对我来说似乎很奇怪。导出包不知道 using 包是否已经定义了同名符号,当然也不能保证它是唯一导出同名符号的包。

由此引起的very common problem 同时使用CGILWP::Simple。两个包都导出head() 并导致错误。我知道,这很容易解决,但这不是重点。您不必使用变通方法来使用两个实际核心的 Perl 库。

据我所知,这样做的唯一原因是懒惰。您可以通过不键入 Foo:: 或使用对象接口来节省一些击键,但这真的值得吗?

【问题讨论】:

  • 你在这里谈论 Perl。保存击键对 Perl 人来说很重要。所以 perl 的人会对你的问题回答“是的!是的!是的!它是”。
  • 我不会回答这个问题,而且我认为我是一个 Perl 人。这不是我们在骆驼神庙拜拜并互相念经的宗教。
  • @Shhnap 这是一个无知的狙击手。

标签: perl


【解决方案1】:

默认情况下从一个模块中导出所有函数的做法对于 Perl 来说是不推荐的做法。如果你有充分的理由,你应该只导出函数。推荐的做法是使用EXPORT_OK,这样用户就必须输入所需函数的名称,例如:

use My::Module 'my_function';

很久以前的模块,如 LWP::Simple 和 CGI​​,是在此建议出台之前编写的,现在很难将它们更改为不导出内容,因为它会破坏现有软件。我猜这个建议是通过人们注意到这样的问题而提出的。

无论如何,Perl 的面向对象对象或它所称的任何东西都不需要你导出任何东西,你也不必说 $foo->,所以你的那部分问题是错误的。

【讨论】:

  • 他说这样可以节省空间。但是,是的,$foo-> 位在技术上是不正确的,并不是真正指代命名空间。
  • $foo-> 如:my $foo = new Blah::Whatever; $foo->bar();
  • Exporter 说“不要在没有充分理由的情况下默认导出任何其他内容!”人们似乎忽略了“没有充分理由”的部分。如果您有一个由用户调用少数函数的模块,请默认导出它们!将默认设置为常见情况。不要让 CGI 这样的怪物让你永远无法导出任何东西。
  • 我稍微改动了一下。你对此满意吗?
  • 我认为它源于 Perl 在 Unix 脚本中的起源。软件包将它们的命令(可执行文件)导出到您的 $PATH 是一个规则。 Perl 4.036 基本上仍然是一个巨大的内置函数包,不仅取代了标准的 Unix 实用程序,还取代了 C 库调用和 Unix 系统调用。没有命名空间。有一个命名空间机制(包),但它很弱且不受欢迎。 Perl 5 和 CPAN 改变了这一切,但只是逐渐地。 CGI 是第一批真正 OO 的 Perl 代码之一,因此它也提供了更传统的界面。
【解决方案2】:

导出是一项功能。与任何语言中的所有其他功能一样,如果您(ab)过于频繁地使用它或不应该使用它,它可能会导致问题。明智地使用它是好的,否则就不好,就像任何其他功能一样。

在没有很多模块的时代,默认导出似乎并不是一件坏事。然而,在 CPAN 上有 15,000 个包,肯定会发生冲突,这是不幸的。但是,现在修复模块可能会破坏现有代码。每当您做出糟糕的界面选择并将其发布给公众时,即使您不喜欢它,您也会致力于它。

所以,这很糟糕,但就是这样,而且有很多方法可以绕过它。

【讨论】:

    【解决方案3】:

    导出包不知道 using 包是否已经定义了同名符号,它当然不能保证它是唯一导出同名符号的包。

    如果您愿意,我想您的 import() 例程可以检查,但默认的 Exporter.pm 例程不会检查(并且可能不应该,因为它会被大量使用,并且总是检查是否定义 name 将导致严重减速(如果您发现冲突,Exporter 应该做什么?))。

    据我所知,这样做的唯一原因是懒惰。您可以通过不键入 Foo:: 或使用对象接口来节省一些击键,但这真的值得吗?

    Foo:: 还不错,但考虑一下My::Company::Private::Special::Namespace:: - 如果我们只导出一些东西,您的代码会看起来更干净。并非所有内容都可以(或应该)位于顶级命名空间中。

    导出机制在使代码更简洁时应该使用。当命名空间发生冲突时,不应该使用它,因为它显然不会使代码更干净,但否则我喜欢在请求时导出东西。

    【讨论】:

    • 别名模块是过长命名空间的一个很好的解决方案。
    • 这是一个很好的通用解决方案,但是既然我们应该知道会从My::Company::Private::Special::Namespace::导出什么样的东西,我们应该能够准确判断它是否会与我们已经拥有的任何东西发生冲突/正在出口。如果没有冲突,为什么不继续导出呢?
    【解决方案4】:

    这不仅仅是懒惰,也不仅仅是旧模块。以“后现代对象系统”Moose 和流行 ORM 的对象接口Rose::DB::Object 为例。两者都将meta 方法导入useing 包的符号表,以便在该模块中提供功能。

    这与从每个提供同名方法的模块中多重继承的问题没有任何不同,只是您的出身顺序将决定调用该方法的哪个版本(或者您可以定义自己的以某种方式手动将父母双方的特征折叠在一起的覆盖版本。

    就我个人而言,我希望能够将 Rose::DB::Object 与 Moose 结合起来,但这并不是什么大问题:可以创建一个“具有”Rose:: 的 Moose 派生类其中的 DB::Object 派生对象,而不是“是”(即继承自)Rose::DB::Object 的对象。

    【讨论】:

    • 然而,Moose 也使用“no Moose”取消导入它的混乱。一点都不马虎。
    • @brian:确实! 'no Moose' 还做了其他一些非常好的优化。
    【解决方案5】:

    Perl 的“开放”包的优点之一是,如果您对模块作者的设计方式不感兴趣,您可以更改它。

    package LWPS;
    require LWP::Simple;
    
    for my $sub (@LWP::Simple::EXPORT, @LWP::Simple::EXPORT_OK) {
        no strict 'refs';
        *$sub = sub {shift; goto &{'LWP::Simple::' . $sub}};
    }
    
    package main;
    
    my $page = LWPS->get('http://...');
    

    当然在这种情况下,LWP::Simple::get() 可能会更好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-20
      • 2020-02-08
      • 1970-01-01
      • 2016-06-17
      • 2014-06-21
      相关资源
      最近更新 更多