如何不从 perl 包中导出所有函数/方法?

tur*_*tle 3 perl object

我正在使用现有的 perl 模块,我们称之为 Obj。我向 Obj 添加了一些新功能(子例程/方法),但将它们存储在另一个 .pm 中,称为 Foo。但是我不希望 Obj 继承 Foo 的每个子项。

现在我已经阅读了几个小时的 perl 文档并且很困惑。 https://perldoc.perl.org/Exporter#Selecting-What-to-Export 只是说“不要导出方法名称!”

这是一些示例代码,我不想看到 Obj.pm 中的 sub _not_exported:

#!/usr/bin/env perl
use strict;
use warnings;
use diagnostics;

package Foo;# Foo is a new PM file used to extend Obj with collections of subroutine's.
# I want to use hello() from Obj
    #use Exporter 'import';
    use Exporter qw(import);
    our @EXPORT_OK = qw/hello/;

    sub hello {
            my $self = shift;
            $self->{test} = 'Hello world';
    }
    # I'd rather not pollute Obj with this sub
    # or perfreably all subs the begin with a underscore
    # How do I exclude this sub?
    sub _not_exported { 
        my $self = shift;
        return 'Exported';
    }


    #main old code object module 
package Obj; # large old PM file I dont want to change this file much
    # pull in the new module
    use base 'Foo'; 
    # use base seems better than this:
    #import Foo;
    #our @ISA = qw(Foo);
    sub new {
        
            my $self = {};
            bless $self, 'Obj';
            return $self;
    }
    eval { $this = $form->_not_exported() } ; #Should return nothing
    
    sub catch_hash {
        my ($self,$arg) = @_; 
        $arg |= '';
        $self->{test} = 'catch';
    }

    
#Perl script, creates an object using Obj;
package main;
    #use Obj;
    import Obj;

    my $form = Obj->new();
    $form->catch_hash();
    print "Toss? '$form->{test}' \n";
    $form->hello();
    print "\nHello? '$form->{test}'\n";
    my $this = "";
    eval { $this = $form->_not_exported() } ; #Should return nothing
    print "$this\ndone\n";

    
1;
Run Code Online (Sandbox Code Playgroud)

我对 Moo / Moose 等其他选项持开放态度,但不想对旧代码进行太多更改。提前致谢

Sch*_*ern 9

[注:我是 Exporter 的前维护者]

我相信您已经将导出与继承混淆了。这很容易做到,Perl 并没有在“函数”和“方法”之间划清界限,它们只是sub.

tl;dr您不需要导出,这就是继承的工作原理,有一个解决方法。


导出使您可以从包外部调用函数,而无需完全限定它。它会让你Foo::hello像 just 一样打电话hello。导出让 Perl 知道这hello真正意味着helloin package Foo

但这些是方法调用,您可以在类或对象上调用它们。my $foo = Foo->new; $foo->hello。无需导出。Foo->new调用并返回一个new对象。知道在 的类的祖先中寻找方法。您不需要在类中使用导出器,这就是“不导出方法名称”的含义。FooFoo$foo->hellofoo$foo

导出是一种故意复制符号的行为。继承要么全有,要么全无。如果您从一个类继承,您将获得它的所有方法(子方法)。这是继承的结果,继承还有许多其他替代方案,例如组合

在其他面向对象语言中,您可以声明该方法private,但它不会被继承。Perl 没有这个。通常,您只是按照惯例接受它,在方法名称前面加下划线,不记录它,如果有人使用它,那是他们的问题。这通常没问题。

但是您可以使用匿名子函数词法变量创建真正的私有方法。

package Foo;

# Define `new` in Foo. Obj will inherit it.
# Do not hard code the class.
# `new` receives the class new was called on.
sub new {
    my $class = shift;
    return bless {}, $class;
}

sub hello {
    my $self = shift;
    $self->{test} = 'Hello world';
}

# This is a reference to an anonymous function in a lexical variable.
# It can only be seen by the code after this line in this file.
my $private_method = sub { 
    my $self = shift;
    return "private method called by $self with args: @_\n";
};

sub public_method {
    my $self = shift;

    # $private_method can be seen here.
    # A subroutine reference can be called like a method.
    print $self->$private_method("basset hounds got long ears");
}
Run Code Online (Sandbox Code Playgroud)

在 Obj.pm 中

package Obj;

# parent is a lightweight replacement for base.
use parent 'Foo'; 
Run Code Online (Sandbox Code Playgroud)

Obj 继承了newandhellopublic_methodfrom Foo,但它看不到 $private_method。它只能在 Foo.pm 内看到。

my $obj = Obj->new;

# private method called by Obj=HASH(0x7fcfdb8126d8) with args: basset hounds got long ears
$obj->public_method;
Run Code Online (Sandbox Code Playgroud)

因为public_method是在它可以看到的地方定义的$private_method$obj->public_method尽管是在 Obj 而不是 Foo 的实例上调用,但它仍然可以工作。