【问题标题】:Do Perl subclasses inherit imported modules and pragmas?Perl 子类是否继承导入的模块和编译指示?
【发布时间】:2010-08-11 16:11:10
【问题描述】:

假设您在一个文件中有一个父 Perl 类:

#!/usr/bin/perl
package Foo;
use strict;
use warnings;

use Data::Dumper;

sub new{
    my $class = shift;
    my %self = ();
    return bless %self, $class;
}
1;

和不同文件中的子类:

#!/usr/bin/perl
package Bar;
use base "Foo";
1;

子类会继承父类的 use 语句吗?我知道方法 new 将被继承。

基本上,我试图减少代码中的样板数量,但我找不到这个问题的明确答案。

【问题讨论】:

  • 我询问此功能是因为 Test::Most 和 Moose 声称可以这样做,但我还没弄清楚他们是如何做到的。
  • 看一下Test::Most中的import()方法。它就是这样做的。它手动加载所有这些样板模块并将它们向上导出两个级别。
  • 感谢大家的帮助!进口潜艇是关键。 (我可能只是为了节省自己未来的痛苦,然后去驼鹿)。
  • 如果你不去 Moose,你可能需要考虑 use parent "Foo"; 而不是 use base

标签: perl


【解决方案1】:

您在评论中询问了 Test::Most 以及它如何减少样板文件。查看它的import 方法。它将模块加载到其命名空间中,将这些符号添加到@EXPORT,然后通过goto 重新调用另一个import,最终将它们放入调用命名空间。这是柯蒂斯在那儿使用的一些严肃的黑魔法,虽然我想知道他为什么不使用像 import_to_level 这样的东西。也许有一些我没有考虑的副作用。


我在Avoid accidently creating methods from module exportsThe Effective Perler 中谈论了很多这类事情。这是在不同的背景下,但它是一些相同的问题。

这是一个不同的例子。

如果其他模块加载了一个模块,您可以访问它。但是,依赖它并不好。以下是三个单独的文件:

Top.pm

use 5.010;

package Top;
use File::Spec;

sub announce { say "Hello from top!" }
1;

Bottom.pm

package Bottom;
use parent qw(Top);

sub catfiles { File::Spec->catfile( @_ ) }

1;

test.pl

use 5.010;

use Bottom;

say Bottom->catfiles( qw(foo bar baz) );

say File::Spec->catfile( qw( one two three ) );

我只在 Top.pm 中加载 File::Spec。但是,一旦加载,我就可以在 Perl 程序的任何地方使用它。输出显示我能够在其他文件中“使用”该模块,即使我只将它加载到一个文件中:

Bottom/foo/bar/baz
one/two/three

为此,加载模块的代码部分必须在代码的任何其他部分尝试使用该模块之前加载。正如我所说,依赖这个是一个坏主意:如果加载顺序发生变化或加载模块消失,事情就会中断。

但是,如果您想导入符号,则必须在要导入的包中显式加载所需的模块。就是这样,导出模块定义了该包中的符号。这与范围无关。

【讨论】:

    【解决方案2】:

    啊,好问题!

    Will the subclass inherit the use statements from the parent? 
    

    这取决于你所说的继承是什么意思。直到最后我不会做出任何假设,但答案是也许。你看,perl 混合了ClassesNamespaces 的思想——package 是一个可以描述其中任何一个的术语。现在的问题是声明use 它所做的只是强制包含一个包,并调用目标import() sub。这意味着它本质上可以无限控制您的包 - 并以此方式控制您的类。

    现在,将它与 perl 中的所有方法组合起来,只不过是 subs,按照惯例将 $self 作为第一个参数,而你只剩下 perl5。对于那些知道如何使用它的人来说,这有一个巨大的好处。虽然 strict 是一个词法编译指示,但 Moose 呢?

    package BigMooseUser;
    use Moose;
    
    package BabyMooseUser;
    our @ISA = 'BigMooseUser';
    
    package Foo;
    my $b = BabyMooseUser->new;
    print $b->meta->name;
    

    现在,BabyMooseUser 从哪里获得构造函数(新)?它是从哪里获得元类的?所有这些都由父类(命名空间)中的单个 use Moose; 提供。所以

    Will the subclass inherit the use statements from the parent?
    

    好吧,在这里,在我们的示例中,如果 use 语句 的效果是添加方法,那肯定是。

    这个主题有点深奥,这取决于你是在谈论 pragma,还是更晦涩的对象框架,还是程序模块。如果您想在 OO 范式中减轻父命名空间影响您自己的命名空间,请参阅namespace::autoclean

    【讨论】:

    • 实际上,使用并不强制包含一个包。它根据您要使用的包名称查找文件()。该文件不必加载该包。按照惯例,我们用它包含的包名来命名我们的文件,但这不是 use() 关心的事情。例如,Module::Build::Version 确实是用来加载版本的。文件 use() 加载甚至可能有多个包。
    • BabyMooseUser 得到它的 new() 因为你声明了一个继承关系。单个 Moose 呼叫不会为您设置。而且, autoclean 只会处理导入的符号。它不会限制对您想要访问的任何其他内容的访问。
    • 说方法是 $self 作为第一个参数的 subs 有点奇怪。方法是子方法,但您最终将它们的所指对象作为第一个参数而没有明确指定它作为参数。该引用对象可能是类名(字符串)或对对象的引用,具体取决于 -> 左侧的内容(忽略间接调用)。它也不像在左侧查找文字或变量那么简单。 :)
    • @brian d foy,我想我已经很清楚了All of this is provided from a single use Moose; in the parent class (namespace). 在问题parent perl class 中理解了继承关系。如果您觉得措辞不够清楚,或者您想在我的答案中添加您自己没有的内容,请使用编辑按钮。
    • 你认为它更好——但我的海报认为更好。我会拿我的。您似乎想不停地争论,并且在技术细节上无济于事,而这些答案并不是问题所在。我只会不理你。
    【解决方案3】:

    为了减少样板代码,我有两个策略:我的大部分课程都是Moose 课程,它负责设置面向对象并给我严格和警告。如果我想在许多包中提供可用的功能,我将创建一个特定于项目的MyProject::Util 模块,它使用Sub-Exporter 为我提供我自己的功能和我自己的界面。这使它更加一致,如果我决定稍后出于任何原因更改 Dumper(例如),我不必更改大量代码。这也将允许您对导出进行分组。一个类通常看起来像这样:

    package Foo;
    use Moose;
    use MyProject::Util qw( :parsing :logging );
    
    use namespace::autoclean;
    
    # class implementation goes here
    
    1;
    

    如果还有其他你认为是样板的东西,并且想要更简单地包含,那当然取决于这些东西是什么。

    【讨论】:

      【解决方案4】:

      对您的问题的务实回答:要么使用,要么查看Modern::Perl 如何执行严格和警告。

      【讨论】:

      • Modern::Perl 引入了严格和警告,这些模块与 $^H 配合使用。大多数模块都不想这样做。
      【解决方案5】:

      您可以通过检查每个包的符号表得到明确的答案:

      # examine-symbol-tables.pl
      use Bar;
      
      %parent_names = map{$_ => 1} keys %Foo::;
      %child_names = map{$_ => 1} keys %Bar::;
      
      delete $parent_names{$_} && ($common_names{$_} = delete $child_names{$_}) foreach keys %child_names;
      
      print "Common names in symbol tables:\n";
      print "@{[keys %common_names]}\n\n";
      
      print "Unique names in Bar symbol table:\n";
      print "@{[keys %child_names]}\n\n";
      
      print "Unique names in Foo symbol table:\n";
      print "@{[keys %parent_names]}\n\n";
      

      $ perl 继承.pl 符号表中的常用名称: 开始 条形符号表中的唯一名称: ISA isa 导入 Foo 符号表中的唯一名称: 自卸车新版本

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-23
        • 1970-01-01
        • 2014-01-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多