最简单也是最可能正确的答案是,最后一个导出给定函数的加载模块将是使用该函数的模块。
导出函数(或其他符号)时,将修改接收包的符号表。因此,如果两个导入函数对表进行了更改,则最后一个更改是保留的。
回想一下
use Foo;
是
或多或少
相当于
BEGIN { require Foo; Foo->import if Foo->can(import); }
.
自从
import
只是一个具有特殊名称的子程序,唯一的限制是J.Random黑客的扭曲想象。
进口
可以是
任何
代码。它可以做任何事,也可以不做任何事。例如,它可以包含逻辑,以确保没有已经定义的函数被重写。
但是,除非有任何奇怪的地方,最后一个装载的将是使用中的那个。
100%技术正确,
使用foo;
等于
BEGIN { require Foo; Foo->import; }
.
但这段代码并不像您期望的那样工作。
具体的代码与我的示例不同。
上面的代码所做的工作与Perl中方法解析的工作方式有关。
通常,一个电话
Foo->some_sub
哪里
some_sub
不存在并且
AUTOLOAD
函数在中定义
Foo
,
一些潜水艇
将由
自动加载
功能。有一个特殊的案例
进口
免除了它
自动加载
检查。见
perlsub on Autoloading
.
后
自动加载
已经(或者真的没有)被检查过,我们检查继承。对匹配函数或
自动加载
对原始包中的每个项重复函数
@ISA
. 这是一个递归过程,包括父代的父代等等,直到检查整个继承树为止。--
“伊萨”
从左到右,深度优先。一直以来,
自动加载
异常已存在,将跳过
进口
.
最后,如果找不到匹配方法,我们就放弃了
UNIVERSAL
通用基类。如果函数存在于
通用
,它被调用,否则我们抛出一个异常,说明找不到函数。
所以,在我们的情况下
Foo->import;
呼叫,
UNIVERSAL::import
调用以处理该作业。那么,这个函数是做什么的?
在Perl5.12.2中,代码如下:
sub import {
return unless $_[0] eq __PACKAGE__;
return unless @_ > 1;
require warnings;
warnings::warnif(
'deprecated',
'UNIVERSAL->import is deprecated and will be removed in a future perl',
);
goto &Exporter::import;
}
请注意,函数所做的第一件事是,如果没有调用它,它将退出。
通用
.
现在我所说的一切都是真的,只要你不做几件事:
-
重写方法解析顺序。如果您这样做,那么无论您如何定义方法解析,都会发生。无论我们是否走向宇宙,或者什么时候完全在空中,并服从于你的奇想。
-
覆盖
通用::导入
. 您可以通过monkey补丁universal::import来做任何您想做的事情。再说一次,这种行为完全取决于你的一时冲动。
所以,我上面给出的半等价代码只是一个简单的简写。我认为这会更容易理解,因为它不需要知道Perl是如何做事情的那么多细节,但是它并不完全等同于实际发生的事情。做一些意想不到的事情会破坏等价性。
我的代码与完全等效的代码不同
此外,我的代码调用
Foo->can
通常会回到
UNIVERSAL::can
. 在任何地方都不是
can
在正常的事件链中调用。当我们考虑到
可以
在Perl中。
-
可以
可以被继承图中的任何类重写或重新实现。哪个
可以
调用的对象取决于方法解析顺序。所有多重继承的问题都适用于这里。
-
可以
看不到自动加载的函数。由于自动加载不适用于导入,这看起来可能不是什么大问题。问题是超载被认为是良好的做法
可以
如果使用自动加载,请考虑到这一点。所以,这使上述问题更加复杂。最好是使用非核心模块
NEXT
启用方法重新分配,以便链中的每个模块都可以处理该方法。不幸的是,这很少发生。
结论
所有这些都是一件非常值得咀嚼的事情。
你可以接受我的速记,知道在某些情况下,它是不完全正确的。
或者您可以接受具有自己的异常集的实际代码。
不管怎样,如果你突破了这两个例子的表面,就会有一些微妙的问题需要处理。