可能重复:
In Perl, how can I check from which module a given function was imported?
我正在使用一些遗留代码,其中多个(!)XML解析模块已经use
d。
这意味着具有相同名称的方法彼此践踏。
有没有一种简单的方法来判断特定方法属于哪个包?
答案 0 :(得分:1)
简单而且最可能正确的答案是,导出给定函数的最后一个模块将是使用其函数的模块。
导出函数(或其他符号)时,将修改接收包的符号表。因此,如果两个导入函数对表进行更改,则最后一个更改是保留的内容。
回想一下,use Foo;
或多或少等同于BEGIN { require Foo; Foo->import if Foo->can(import); }
。
由于import
只是一个具有特殊名称的子程序,唯一的限制是J. Random Hacker扭曲的想象力。 import
可以是任何代码。它可以做任何事情。例如,它可以包含逻辑,以确保没有已经定义的函数被覆盖。
但是,除非有任何奇怪之处,否则最后加载的将是正在使用的那个。
要在技术上100%正确,use Foo;
相当于BEGIN { require Foo; Foo->import; }
。
除了此代码不能像您预期的那样工作。
确切代码与我的示例有所不同。
以上代码的作用与Perl中方法解析的工作方式有关。
通常情况下,调用Foo->some_sub
,其中some_sub
不存在且AUTOLOAD
函数在Foo
中定义,some_sub
将由{处理{1}}功能。 AUTOLOAD
有一个特例,可以免除import
次检查。见perlsub on Autoloading。
AUTOLOAD
已经(或实际上没有)检查过后,我们会检查继承。对原始包AUTOLOAD
中的每个项重复检查匹配函数或AUTOLOAD
函数。这是一个递归过程,包括父母的父母等等,直到检查整个继承树 - 从左到右,深度优先顺序检查@ISA
。所有这一切@ISA
例外都已到位,AUTOLOAD
将跳过该例外。
最终,如果找不到匹配的方法,我们会回到import
通用基类。如果函数存在于UNIVERSAL
中,则调用它,否则我们抛出一个异常,表示无法找到该函数。
因此,在我们UNIVERSAL
调用的情况下,调用Foo->import;
来处理该作业。那么,这个功能有什么作用呢?
在Perl 5.12.2中,代码如下所示:
UNIVERSAL::import
请注意,如果没有在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;
}
上调用该函数,那么函数所做的第一件事就是纾困。
现在我所说的一切都是真的,只要你不做几件事:
覆盖方法解析顺序。如果您这样做,那么无论您已将其定义为方法,都会发生方法解析。无论我们是否进入UNIVERSAL,或者什么时候完全在空中并受到你的想法。
覆盖UNIVERSAL
。你可以猴子修补UNIVERSAL :: import来做任何你想做的事情。同样,这将如何表现完全取决于你的想法。
所以,我上面给出的半等效代码只是简单的简写。我认为它会更容易理解,因为它不需要知道Perl如何处理事情的细节,但它并不是100%等同于真正发生的事情。做意想不到的事情打破了等价。
我的代码与完全等效代码不同
此外,我的代码调用UNIVERSAL::import
,通常会回退到Foo->can
。在正常的事件链中,UNIVERSAL::can
无法调用。当我们在Perl中考虑can
的问题时,这会特别毛茸茸。
can
可能会被继承图中的任何类覆盖或重新实现。调用哪个can
取决于方法解析顺序。多重继承的所有问题都适用于此。can
没有看到自动加载的功能。由于自动加载不适用于导入,这似乎不是什么大问题。问题是,如果您使用自动加载,将can
重载为考虑这一点被认为是一种好习惯。因此,这加剧了上述问题。最好的办法是使用非核心模块can
来启用方法redispatch,这样就可以由链中的每个模块处理。不幸的是,很少这样做。<强>结论强>
所有这一切都是可以咀嚼的。
你可以接受我的速记,知道在某些情况下,它不完全正确。
或者您可以接受实际代码,它有自己的一组例外。
无论哪种方式,如果你突破任何一个例子的表面,都有一些微妙的问题需要处理。