如何不从 perl 中的包中导出所有 functions/methods?

How to not export all functions/methods from a package in perl?

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

现在我已经阅读了几个小时的 perl 文档并且感到困惑。 https://perldoc.perl.org/Exporter#Selecting-What-to-Export 只是说 'Do not export method names!'

这是一些示例代码,我不想看到来自 Obj.pm 的子 _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;

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

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

我相信您混淆了导出和继承。这很容易做到,Perl 不会在“函数”和“方法”之间划一条线,它们只是 sub.

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


导出允许您从包外部调用函数而无需完全限定它。它会让你调用 Foo::hello 就像 hello 一样。导出让 Perl 知道 hello 真正意味着包 Foo.

中的 hello

但这些是方法调用,您可以在 class 或对象上调用它们。 my $foo = Foo->new; $foo->hello。无需导出。 Foo->newFoo 和 returns 中调用 new 一个 Foo 对象。 $foo->hello 知道在 $foo 的 class 的祖先中寻找方法 foo。您不需要在 class 中使用导出器,这就是“不导出方法名称”的意思。

导出是一种故意复制符号的行为。继承是全有或全无。如果您从 class 继承,您将获得它的所有方法(subs)。这是继承的结果,还有许多其他继承替代方案,例如 composition.

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

但是您可以使用 anonymous subs and lexical variables 来创建真正私有的方法。

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");
}

并且在Obj.pm

package Obj;

# parent is a lightweight replacement for base.
use parent 'Foo'; 

Obj 从 Foo 继承了 newhello 以及 public_method,但它看不到 $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;

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