Perl Exporter在子类

时间:2017-12-13 23:09:51

标签: perl perl-exporter

我已经找了最后几个小时,我很难过。

我确实看到How can I share variables between a base class and subclass in Perl?,但它没有回答我的问题

我正在尝试编写一个实用程序模块(此时用于测试目的),以便在内部开发的脚本中使用。但我似乎很难让出口商实际出口我需要的东西。

我的内容如下:

LIB / Internal.pm:

package Internal;

use     5.008_008;
use     strict;
use     warnings;
use     Exporter;

our $VERSION = 1;
our @EXPORT_OK = qw($EMPTY $NOW);
our %EXPORT_TAGS = ( ':all' => \@EXPORT_OK );

our $EMPTY = q{};
our $NOW = time;

1;

LIB /内部/ Utility.pm:

package Internal::Utility;

use     5.008_008;
use     strict;
use     warnings;
use     FindBin::libs;
use     parent qw(Internal);
use     Data::Dumper;

our $VERSION = 1;
our @EXPORT_OK = qw(run);
our %EXPORT_TAGS = ( ':all' => \@EXPORT_OK );

sub run
{
    print Dumper $NOW;
    print Dumper $EMPTY;
    exit;
}

1;

仓/ test.pl:

#!/usr/bin/env perl

use     5.008_008;
use     strict;
use     warnings;
use     FindBin::libs;
use     Internal::Utility qw(:all);

run();
exit;

当我运行test.pl时,我收到以下错误:

Global symbol "$NOW" requires explicit package name at lib/Internal/Utility.pm line 8.
Global symbol "$EMPTY" requires explicit package name at lib/Internal/Utility.pm line 9.
Compilation failed in require at ./test.pl line 7.
BEGIN failed--compilation aborted at ./test.pl line 7.

为什么Internal :: Utility中的use parent没有从Internal.pm导入变量?有没有办法告诉使用父母明确导入一些变量?

3 个答案:

答案 0 :(得分:3)

您正试图在模块的模块中继承"和use parentparent

  

在编译时与基础建立ISA关系

(我的重点)。在派生类中设置基类的使用是一种实用工具,InternalInternal::Utility不是类(它们不是bless)。最后请看注释。

将它们写为类。 (然后你不需要考虑导出符号。)这将为你提供更多的能力和控制。

至于Exporter,你必须

use Exporter 'import';

以便子import可供模块使用。

一个名为" import"用于将您在@EXPORT_OK等中指定的符号推送给调用者。很好,一个模块可以从Exporter借用它,而不是实现它自己的。{1}}。但Exporter默认情况下不会将其导出。

使用类的示例,显示了一些使常量可用的方法,以及一些其他位。当你选择一种方式(并删除其他人的代码)并删除打印时,它会变得更简单;最长的Internal::Utility类缩小到几行。

基类Internal.pm

package Internal;

use warnings;
use strict;

# Set up constants for derived classes to use, for demo 
# BOTH via class data and method, and via an attribute

our $NOW = time;
sub get_NOW { return $NOW }

sub new { 
    my ($class, @pars) = @_;
    # Set up object's data ("attributes"), using @pars
    my $self = { _now => $NOW };  # ...
    # Make $self an object and this package a class
    return bless $self, $class;
}

sub other_methods { }

1;

派生类(子类),Internal/Utility.pm

package Internal::Utility;

use warnings;
use strict;
use feature 'say';

use parent 'Internal';  # now have everything from 'Internal' class

# Retrieve constant from parent, if you *must*
# It is much better to use a method or an attribute 
use mro; 
my $NOW = do {
    my $Parent = (__PACKAGE__)->mro::get_linear_isa->[1];
    my $now = $Parent . '::NOW';
    no strict 'refs';
    $$now;
};  
my $Now = $Internal::NOW;  # or using hard-coded parent name

# Constructor is also inherited (can change or override it altogether)

sub print_start_time {
    my $self = $_[0];
    say "\tIn ", __PACKAGE__, ". Start time:";
    say "\tObject's method (inherited): ", $self->get_NOW;
    say "\tObject's data (inherited):   ", $self->{_now};
    say "\tDirectly, as class method:   ", Internal->get_NOW;
    say "\tClass variable, from parent: $NOW";
    say "\tOther class variable:        $Now";
};

1;

驱动程序main.pl

use warnings;
use strict;
use feature 'say';

use Internal::Utility;

my $obj = Internal::Utility->new();

say "Created ", ref $obj, " object";

$obj->print_start_time();

say "class constant:   $Internal::NOW";
say "as object method: ", $obj->get_NOW;
say "as class method:  ", Internal->get_NOW;

打印

Created Internal::Utility object
        In Internal::Utility. Start time:
        Object's data (inherited):   1513229709
        Object's method (inherited): 1513229709
        Directly, as class method:   1513229709
        Class variable, from parent: 1513229709
        Other class variable:        1513229709
class constant:   1513229709
as object method: 1513229709
as class method:  1513229709

最好简单地使用父类的属性或其方法,这些属性或其方法都由其子类继承。如果您使用某个属性,那么在每个使用它的子类中为这些子类的用户提供一个访问器。

这两者之间存在差异,可供选择取决于设计细节。作为一个基本的我会采用这种方法。请记住,父类的类变量可供其派生类的任何用户访问。

一旦你开始使用Perl中的对象,我建议熟悉它的原生系统。但是,在某些时候,我应该研究Moose  和/或其轻量级兄弟Moo

有关课程与对象(实例)方法的说明

A"类方法"特别是与任何一个对象无关,而是与整个类有关(比如,返回创建到该点的多个对象);因此它不需要对象的数据。当在类名上调用一个方法时,它是作为第一个参数传递给它的类名,这就是使它成为"类"方法。

"对象(实例)方法"意味着为特定对象做工作,并且当在对象上调用时,它是传递给它的对象(对用于存储该类中的数据的Perl数据结构的引用)。

也许令人困惑的是,我们可以在一个对象上调用一个方法作为一个类方法,没有错误(除非该方法使用现在它不具备的类名);这不应该是因为它弄乱了目的。使用类名称调用实例意味着的方法会很糟糕,因为它很可能期望传递一个对象,而它将获得一个带有类名的普通字符串。

在这个简单的演示中,这种明显的模糊性可能会更加混乱,因为get_NOW可以在类和对象上同样被调用。它的意图显然是一种类方法。但它也可以用于一个对象,在继承的类中,方法不具有父级的名称。

关于包裹和类的说明

A package

  

将BLOCK或编译单元的其余部分声明为在给定的命名空间中。

然后在其中定义sub,并提供一种方法将这些符号(名称)导出到调用者,以便他们可以方便地使用它们。你有一个功能库,就是这样。这通常被称为"模块,"这是我如何使用上述术语。查看更多in perlmod

是代码/命名空间(缺少更好的名词),可以实例化,这意味着您可以创建实例< / em>,它是一个对象。这个对象&#34;知道&#34;它与哪个类相关联,以便它可以使用该类中提供的子,使它们成为方法。系统还为类提供了一种设置数据(属性)的方法,每个对象都有自己的副本。请参阅perlootut

在Perl中,类被定义为()一个包。是什么使它成为一个类,以便可以为它实例化一个对象,就是有一个子程序,其中一个引用(通常是一个哈希)被bless编入&#34;进入&#34;包(bless HASHREF, PACKAGENAME)并返回。 perlobj说(我的重点)

  

对象只是与特定类明确关联的Perl数据结构(散列,数组,标量,文件句柄等)。

object bless - ed和返回的hashref)中有一个OBJECT标志,除了hashref内部的其他属性之外。因此,当它被解除引用时,解释器可以告诉应该在该包中寻找子应用程序;因此,这是一个阶级的对象。

请注意,Perl中的类通常也称为&#34;模块,&#34;什么可能引起混淆。

因此这个问题中的模块不是类。那么&#34;继承,&#34;典型的面向对象术语?由于Perl的OO系统是极简主义的,因此一些设施适用于通用模块和类,并且术语也可能在这里或那里变得模糊。例如,&#34;继承&#34;可以使用其更通用的含义,其中一个单位在另一个单位中获取设置。

我仍然建议不要在不属于类的模块上使用parent pragma,即使是it works

答案 1 :(得分:2)

在Perl中,继承仅影响方法调用的解析方式。其他所有东西 - 变量,子程序等 - 都保持不变。

这意味着您应该将这些符号导入到派生类中,就像您从任何其他模块导入一样:

package Internal::Utility;
...
use Internal ':all';
use parent qw(Internal);
...

Perl使用包作为其唯一的命名空间机制,但包也可以用作类。这可能有点令人困惑。作为一般建议,你绝不应该将这两者混合在一起:一个软件包应该提供一个面向对象的接口或一个基于导出器的程序接口。混合两者往往会导致误导性设计。

答案 2 :(得分:1)

您不能use Exporter并期望它能够正常运作。在您的Internal包中,您需要使用

继承
use Exporter;
our @ISA = 'Exporter';

use parent 'Exporter';

或者,对于该模块的更新版本,您只需导入import子例程

use Exporter 'import';

如果您需要特别的东西,可以编写您自己的import,但在大多数情况下,没有必要