如果我有一个带有方法的模块a
并且b
想要导出它们,我会这样做:
use Exporter;
our @ISA = qw (Exporter);
our @EXPORT = qw (a b);
我不明白的是这条线:
our @ISA = qw (Exporter);
做什么?
如果我有一个带有方法的模块a
并且b
想要导出它们,我会这样做:
use Exporter;
our @ISA = qw (Exporter);
our @EXPORT = qw (a b);
我不明白的是这条线:
our @ISA = qw (Exporter);
做什么?
@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
。
在这里考虑面向对象。
将 Exporter 视为一个模块,而不是一个类。将ISA
“是”视为“我的模块是导出器的子类”中的意思。
您正在做的是将您的模块声明为 Exporter 类的子类,这意味着您可以使用import
Exporter 类的方法,这很有用。
要真正解释 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 use
File::Find`。
Exporter 的import
方法所做的是将您的子例程(如果您愿意的话,您的变量)从您的命名空间复制到当前的命名空间。想象一下使用File::Copy
模块而不能将子例程复制到copy
主命名空间。你仍然可以使用它,但你必须给它命名空间的名称:
use File::Copy;
...
File::Copy::copy( $from_file, $to_file );
多亏了 Exporter(和 import 方法),您放入 packages@EXPORT
数组的任何子例程都被复制到当前命名空间。因此,您可以像这样访问 File::Copy s
copy` 子例程:
use File::Copy;
...
copy ( $from_file, $to_file );
这也是您必须将所有这些变量声明为our
和非的原因my
。my
变量是词法范围的,不能在声明它们的地方之外访问。包变量(如$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我们在这里讨论包变量。包变量随处可见。
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
直接设置。parent
pragma(以及旧的,现在不鼓励使用的pragma base
)声明继承关系并为您设置此变量。
@ISA
包变量用于指定类之间的继承。不鼓励您自己操作此变量。如果要从另一个类继承,请执行
use parent 'Parent::Class';
或 v10.1 之前的版本:
use base 'Parent::Class';
在这种特定情况下,继承自Exporter
使该import
方法可用。您的代码可以重写为。
use parent 'Exporter';
our @EXPORT = qw/a b/;
该import
方法在您的模块为use
d 时自动调用,并可能将符号导出到 using 包。
为什么@ISA
手动使用不好:
不要分配给@ISA
:其他模块可能已经添加了条目;这将覆盖它们。因此,push @ISA, "Some::Class"
。
避免@ISA
在运行时修改。最好尽早指定继承关系(在解析期间或初始编译后),以便您的模块可以不受限制地在那里使用。您可以将其包装在一个BEGIN
或CHECK
块中,例如
BEGIN {
push @ISA, "Some::Class";
}
通过继承parent
使这更容易。parent
如果请求的模块尚未加载,也将编译它:不需要use Some::Class
.