7

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

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

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

4

4 回答 4

15

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

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

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

use parent 'Exporter';

@ISA它为您在幕后进行调整,还做了一些其他有用的事情。

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

our @ISA = qw(Exporter)

你将你的类声明为Exporter. 该类Exporter提供了一个名为import. 你为什么要那个?因为当你说

use MyClass;

实际发生的是:

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

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

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

use Exporter qw(import);

并且您的包裹将获得该import方法,而无需完全弄乱@ISA

于 2013-07-29T19:15:12.697 回答
8

在这里考虑面向对象

将 Exporter 视为一个模块,而不是一个。将ISA“是”视为“我的模块导出器的子类”中的意思

您正在做的是将您的模块声明为 Exporter 类的子类,这意味着您可以使用importExporter 类的方法,这很有用。

要真正解释 Exporter 在做什么,您必须了解 Perl 使用命名空间。想象一下,如果您的程序有一个名为1$total的变量,但您正在使用的模块也是如此。您的$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::name and$File::Find::dir variables you have when you useFile::Find`。

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

use File::Copy;

...

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

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

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我们在这里讨论变量。包变量随处可见。

于 2013-07-29T20:27:48.120 回答
3

Exporter当 Perl在你的包中找不到方法时,它指示 Perl 在模块中查找方法。您想要继承的常用方法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 所提到的,很少需要@ISA直接设置。parentpragma(以及旧的,现在不鼓励使用的pragma base)声明继承关系并为您设置此变量。

于 2013-07-29T19:13:14.867 回答
3

@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.

于 2013-07-29T19:00:47.597 回答