【问题标题】:What is @ISA in Perl?Perl 中的@ISA 是什么?
【发布时间】:2013-07-29 17:05:11
【问题描述】:

如果我有一个带有 ab 方法的模块并且想要导出它们,我会这样做:

use Exporter;  
our @ISA = qw (Exporter);  
our @EXPORT = qw (a b);  

我不明白的是这行:
our @ISA = qw (Exporter); 是做什么的?

【问题讨论】:

  • perldoc perlobjperldoc perltoot
  • 导出方法没有意义?!?你的意思是“潜艇”吗?

标签: perl perl-module


【解决方案1】:

@Foo::ISA 数组是一个全局变量,用于保存类Foo 所继承的类。

一般来说,你不应该直接操纵@ISA;这样做表明您可能正在查看旧的 Perl 代码。

如果要声明继承关系,更好的做法是

use parent 'Exporter';

它会在幕后为您调整 @ISA 并做一些其他有用的事情。

在你的情况下,当你这样做时

our @ISA = qw(Exporter)

您将您的类声明为Exporter 的子类。 Exporter 类提供了一个名为import 的方法。你为什么要那个?因为当你说

use MyClass;

实际发生的是:

BEGIN { 
    require 'MyClass.pm';
    MyClass->import;
};

每次有人uses 你的班级时,都会自动调用import 方法。这种方法可以做任何你想做的事情。 (如果您愿意,您可以编写自己的import)但通常它用于将符号导入调用者的命名空间。这就是 Exporter 为您所做的。

不过,Exporter 也导出了自己的 import 方法,因此您实际上不必再继承它了。 (这是很久以前修复的。)所以现在你可以说

use Exporter qw(import);

您的包将获得import 方法,而完全不必与@ISA 混淆。

【讨论】:

    【解决方案2】:

    在这里考虑面向对象

    不要将Exporter 视为一个模块,而应将其视为一个。将ISA 视为“是”,如“我的模块 Exporter 的子类”

    您所做的是将您的模块声明为 Exporter 类的子类,这意味着您可以使用 Exporter 类的 import 方法,这有点用处。

    要真正解释 Exporter 在做什么,您必须了解 Perl 使用命名空间。想象一下,如果您的程序有一个名为$total 的变量1,但您正在使用的模块也是如此。您的 $total 变量会干扰模块的 $total 变量。

    为了防止这种情况,Perl 使用命名空间。您的程序在main 的默认命名空间 中运行。您的模块使用package 函数来声明一个新的命名空间。现在,您和您的模块都可以安全地使用名为$total 的变量。在你的程序中,它实际上是$main::total,而在包中,它是$Package::Name::total. If you want to use something from one _namespace_ in another, you can prepend the _namespace_ on it. Think of the$File::Find::nameand$File::Find::dirvariables you have when you useFile ::查找`。

    Exporter 的import 方法所做的是将您的子例程(如果您愿意的话,您的变量)从您的命名空间复制到当前的命名空间。想象一下使用File::Copy 模块,而不能复制copy 子例程复制到您的主命名空间。你仍然可以使用它,但你必须给它命名空间的名称:

    use File::Copy;
    
    ...
    
    File::Copy::copy( $from_file, $to_file );
    

    感谢 Exporter(和 import 方法),您放入包 @EXPORT 数组的任何子例程都被复制到当前命名空间。因此,您可以像这样访问 File::Copyscopy` 子例程:

    use File::Copy;
    
    ...
    
    copy ( $from_file, $to_file );
    

    这也是为什么您必须将所有这些变量声明为our 而不是mymy 变量是词法范围,不能在它们声明的地方之外访问。包变量(如$File::Find::name)可以。要让Exporter 找到您的@EXPORT@EXPORT_OK 数组,这些必须是 变量。

    现在,需要导出函数...

    我们知道并喜欢导出子例程的最古老的模块。您使用 File::Copy、File::Path、File::Find,并且可以立即访问它们的子例程。这是因为他们将他们的子例程放入@EXPORT 数组中。这曾一度被认为是可取的,因为它使您可以立即使用这些功能。

    File::Temp 等较新的模块要求您声明要导入的子例程:

    use File::Temp qw(tempdir);
    
    ...
    my $temp_dir = tempdir;
    

    如果我没有那个qw(tempdir),我就无法使用tempdir 函数。

    这被认为是礼貌的。该模块正在询问您是否允许导入该函数。这是通过将子例程放入@EXPORT_OK 来完成的。这些只会根据要求导出。

    这更好,因为您不必导入所有内容,只需导入您需要的内容。而且,您正在记录这些函数的定义位置。

    面向对象的模块根本不导出任何东西,也不需要使用 Exporter。很久没有写一个使用 Exporter 的模块了。

    -- 1 我们在这里讨论的是 变量。包变量随处可见。

    【讨论】:

      【解决方案3】:

      当 Perl 在你的包中找不到方法时,它会指示 Perl 在 Exporter 模块中查找方法。您希望从Exporter 继承的常用方法是它的import 方法,这是将模块的导出符号复制到调用包的工作发生的地方。

      来自perlobj

      每个包都包含一个名为@ISA 的特殊数组。 @ISA 数组包含该类的父类的列表(如果有)。当 Perl 进行方法解析时会检查这个数组,我们稍后会介绍。

      例如,这是一个错误,因为 Perl 将尝试调用不存在的子例程 Foo::some_method

      sub Bar::some_method { 42 }
      my $obj = bless {}, 'Foo';
      $obj->some_method;
      

      包中的@ISA 变量告诉Perl 在其他包中查找该方法,因此这段代码将工作并调用Bar::some_method 方法。

      sub Bar::some_method { 42 }
      my $obj = bless {}, 'Foo';
      @Foo::ISA = qw(Bar);
      $obj->some_method;
      

      这方面的实际应用是继承。

      正如 amon 所提到的,很少需要直接设置@ISAparent pragma(以及较旧的,现在不鼓励使用的 base pragma)声明继承关系并为您设置此变量。

      【讨论】:

        【解决方案4】:

        @ISA 包变量用于指定类之间的继承。不鼓励您自己操作此变量。如果你想从另一个类继承,做

        use parent 'Parent::Class';
        

        或 v10.1 之前的版本:

        use base 'Parent::Class';
        

        在这种特定情况下,从Exporter 继承使import 方法可用。您的代码可以重写为。

        use parent 'Exporter';
        our @EXPORT = qw/a b/;
        

        import 方法在你的模块为used 时会自动调用,并且可能会将符号导出到 using 包中。


        为什么手动使用@ISA 不好:

        • 不要分配给@ISA:其他模块可能已经添加了条目;这将覆盖它们。因此,push @ISA, "Some::Class"

        • 避免在运行时修改@ISA。最好尽早指定继承关系(在解析期间或初始编译后),以便您的模块可以不受限制地在那里使用。您可以将其包装在 BEGINCHECK 块中,例如

          BEGIN {
            push @ISA, "Some::Class";
          }
          

        通过parent 继承使这更容易。 parent 也将编译请求的模块,如果它尚未加载:不需要use Some::Class

        【讨论】:

        • 但是为什么我需要明确地写出来呢?之后我就不用了
        • @Jim 我不明白。 Exporter 模块允许您在包为used 时从包中导出符号。这是通过import 方法实现的。您可以自己编写一个(容易出错)或从Exporter 继承一个通用实现。继承在内部用 @ISA 数组表示。
        猜你喜欢
        • 2012-12-20
        • 1970-01-01
        • 2010-11-25
        • 1970-01-01
        • 2010-10-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多