如何判断Perl方法属于哪个包?

时间:2010-11-27 10:28:34

标签: perl perl-module

  

可能重复:
  In Perl, how can I check from which module a given function was imported?

我正在使用一些遗留代码,其中多个(!)XML解析模块已经use d。

这意味着具有相同名称的方法彼此践踏。

有没有一种简单的方法来判断特定方法属于哪个包?

1 个答案:

答案 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,这样就可以由链中的每个模块处理。不幸的是,很少这样做。

<强>结论

所有这一切都是可以咀嚼的。

你可以接受我的速记,知道在某些情况下,它不完全正确。

或者您可以接受实际代码,它有自己的一组例外。

无论哪种方式,如果你突破任何一个例子的表面,都有一些微妙的问题需要处理。