gpt4 book ai didi

perl - 要求使用别名的 Perl 模块

转载 作者:行者123 更新时间:2023-12-04 14:22:11 26 4
gpt4 key购买 nike

***以下是帮助解释我到目前为止所尝试的背景。如果您想先阅读主要问题,请跳到底部。***

开始

我的 Baz模块调用许多其他模块,所有模块都相似,每个模块都在命名空间中向下一层。这里感兴趣的组成Thing角色。除了个人require语句,常量列表 ALL_THINGS枚举相关 Thing模块供以后使用。我的原始代码如下所示:

package Foo::Bar::Baz;

use constant ALL_THINGS => qw{ Foo::Bar::Baz::ThingA Foo::Bar::Baz::ThingB ... };

require Foo::Bar::Baz::ThingA;
require Foo::Bar::Baz::ThingB;
[...]

消除冗余

正如我所提到的,有很多 Thing模块,我还在添加更多。每次我创建一个新的 Thing类,我必须添加一个新的 require声明,并将相同的文本添加到 ALL_THINGS列表。为了避免这种重复,我想替换个人 require循环遍历 ALL_THINGS 的行.我添加了这个,它本身就可以正常工作:

foreach my $module (ALL_THINGS) {
eval "require $module";
}

但是,此解决方案似乎不适用于我的下一个更改。

提高可读性

每个 Thing 的完整模块名称又长又笨重。我想为包名称起别名,以便于输入/阅读。我看了 Package::Alias , 但似乎会 use他们,如果可能的话,我想避免。到目前为止,我找到的最佳解决方案是 this question 中建议的模式。 :

BEGIN { *Things:: = *Foo::Bar::Baz:: ; }

这也有效,因为它允许我使用 Thing::ThingA->classMethod .然而,不出所料,它在 require 中不起作用。在上面循环,如 require Thing::ThingA搜索 @INC对于 Thing/ThingA.pm而不是 Foo/Bar/Baz/ThingA.pm .

主要问题:将它们放在一起

我想减少我的 Foo::Bar::Baz::ThingA 中的长包名称(即 ALL_THINGS )列表到 Things::ThingA ,但仍然可以使用相同的列表来构建我的 require循环中的语句。
  • 是否有不同的别名 Foo::Bar::Baz::Things::这样我就可以 require Things::ThingA ?
  • 或者,如果我做对了别名部分,有没有办法取消引用 Things::ThingAFoo::Bar::Baz::ThingA在(或之前?)评估中,以便 require找到正确的包?
  • 是否有一些其他普遍接受的方法将同一命名空间的不同级别的包捆绑在一起,以消除对所有这些的需要?

  • 额外问题(与 eval "require $x" 相关):
  • perldoc for constant它说常量列表实际上不是只读的。使用 eval 是否会产生安全问题? ?
  • 如果是这样,是否有更安全的方法来执行此操作而无需加载其他模块?
  • 作为 Perl 的新手,在这种方法和我以前的方法(每个模块的单独 require 语句)之间我可能会遗漏任何细微的差异吗?


  • 注意:我接受了 Dave Sherohman 的回答,因为它最充分地解决了我提出的问题。但是,我最终根据 lordadmira 的回答实现了一个解决方案。

    最佳答案

    你喜欢你的魔法有多黑?

    我们都知道,为了require模块,Perl 浏览 @INC找到它要加载的文件。这个过程中鲜为人知(甚至更少使用)的方面之一是 @INC不限于仅包含文件系统路径。您还可以将 coderefs 放在那里,允许您劫持模块加载过程并将其弯曲到您的意愿。

    对于您描述的用例,类似以下(未经测试)的东西应该可以解决问题:

    BEGIN { unshift @INC, \&require_things }

    sub require_things {
    my (undef, $filename) = @_;

    # Don't go into an infinite loop when you hit a non-Thing:: module!
    return unless $filename =~ /^Thing::/;

    $filename =~ s/^Thing::/Foo::Bar::Baz::/;
    require $filename;
    }

    基本上它的作用是,作为 @INC 中的第一个条目,它会查看请求模块的名称,如果它以 Thing:: 开头,它会加载相应的 Foo::Bar::Baz::取而代之的是模块。简单有效,但很容易混淆 future 的维护程序员(包括你自己!),所以谨慎使用。

    作为一种替代方法,您还可以选择在模块中指定与文件的物理路径不对应的包名称 - 按照惯例,两者通常相同,以便在阅读和维护代码时更轻松,但它们没有匹配的技术要求。如果文件 ./lib/Foo/Bar/Baz/Xyzzy.pm包含
    package Thing::Xyzzy;

    sub frob { ... };

    那么你会通过这样做来使用它
    require Foo::Bar::Baz::Xyzzy;
    Thing::Xyzzy::frob();

    Perl 会对此非常满意(即使您的同事可能不满意)。

    最后,如果你想摆脱 ALL_THINGS ,看看 Module::Pluggable .您给它一个命名空间,然后它会在该命名空间中找到所有可用的模块并为您提供它们的列表。也可以设置为 require找到的每个模块:
    use Module::Pluggable require => 1, search_path => ['Foo::Bar::Baz'];
    my @plugins = plugins;
    @plugins现在包含所有 Foo::Bar::Baz::* 的列表模块,并且这些模块已经加载了 require .或者您可以调用 plugins如果您只关心加载模块并且不需要它们的列表,则无需将结果分配给变量。

    关于perl - 要求使用别名的 Perl 模块,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55840549/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com