【问题标题】:use symbols from module without package (require)使用没有包的模块中的符号(需要)
【发布时间】:2014-02-20 17:42:33
【问题描述】:

我已经问过一个类似的问题,但它更多地与对函数使用裸词有关。基本上,当我开始学习 perl 中的“包”和 OOP 时,我正在重构我的一些代码。由于我的模块都没有使用“包”,所以我可以将它们“要求”到我的代码中,并且我能够毫无问题地使用它们的变量和函数。但是现在我创建了一个模块,我将用作对象操作的类,并且我希望继续能够以相同的方式在其他模块中使用我的函数。没有多少阅读文档和教程能完全回答我的问题,所以请,如果有人可以解释它是如何工作的,以及为什么,那将有很长的路要走,而不是“这就是你的做法”-键入答案。

也许这可以说明问题:

我的文件.cgi:

require 'common.pm'
&func_c('hi');
print $done;

common.pm:

$done = "bye";
sub func_c {print @_;}

将按预期打印“hi”然后“bye”。

我的文件_obj.cgi:

use common_obj;
&func_obj('hi');
&finish;

common_obj.pm:

package common_obj;
require 'common.pm';
sub func_obj {&func_c(@_);}
sub finish {print $done;}

给出“未定义的子程序func_c ...”

我(有点)了解命名空间等,但我不知道如何在无需修改 common.pm 的情况下实现我想要的结果(让 func_obj 能够从 common.pm 调用 func_c) (这可能会破坏依赖于它工作方式的其他模块和脚本)。我知道在 BEGIN 中将 use 及其 import() 称为“要求”。但同样,这需要修改 common.pm。我想要完成的事情有可能吗?

【问题讨论】:

  • 真的不想这样做。您正在编写 Perl 4 风格的代码,而自 1993 年以来就没有这样做过。
  • 不想做什么?为什么不呢?

标签: perl perl-module


【解决方案1】:

您需要导出包 common_obj 中的符号(目前它不是一个类包)。

您需要熟悉Exporter 模块。 Modern Perl 也有介绍(免费提供的书,但也可以考虑购买)。

这很简单——如果你在@EXPORT_OK 中列出函数,那么uses 你的包的人可以使用它们。您还可以通过EXPORT_TAGS 将函数分组到命名组中。

首先导出几个函数,在您的use 语句中列出这些函数并获取基础知识。那就很容易了。

如果您的模块真的是面向对象的,那么您将通过对象引用$my_obj->some_method(123) 访问这些方法,因此不需要导出。一个包甚至可以同时提供过程/功能和面向对象的接口。

顺便说一句,您用更简洁的东西来包装旧的“不安全”模块的想法似乎是一种明智的做法。在不破坏现有工作代码的情况下掌控一切。


编辑:解释。

如果你需要一段不合格的代码,那么它的定义将最终出现在需要的包 (common_obj) 中,但如果你将代码限制在包定义中,然后 use 它你需要显式导出定义。

【讨论】:

  • 但是从 common_obj 导出符号如何授予它访问 common.pm 中的符号的权限?还是您的意思是 common.pm?那是我不想修改的。是的,我有很多“正常工作”的旧代码(在很多其他脚本使用的 .pm 中),当我尝试更新新东西以使用更好的做事方式(如 OOP)时,我没有不想花费数周时间重构其他所有内容以避免破坏它。
【解决方案2】:

您可以使用 common_obj::func_obj 和 common_obj::finish。你只需要添加他们的命名空间,它就可以工作。在这种情况下,您不需要“&”。

当您使用 package 语句(在 common_obj.pm 中)时,您更改了后续函数的命名空间。如果没有(在 common.pm 中),则将函数包含在同一个命名空间(main 或 common_obj)中。我不相信这与使用/要求有任何关系。

您应该使用导出器。更改 common_obj 以添加:

use base Exporter;
@EXPORT_OK = qw/func_obj finish/;

然后改变myfile_obj:

use common_obj qw/func_obj finish/;

我假设您只是想将新界面添加到旧的“正常工作”模块中。我确信这充满了问题,但如果可以做到,这是一种方法。

【讨论】:

  • 但是func_c是在common.pm中定义的,common_obj不能访问(就是抛出undefined subroutine的错误)。另外,我需要 $done 是所有 4 个文件中的相同变量。
  • 对不起,我的意思是 func_obj。它确实有效,但由于您可以修改 common_obj,您可以在那里使用 Exporter,这是推荐的。我假设您正在后台处理一个真实的对象界面,这只是一个简化。
  • 您不能将函数从 common 导出到 main,但您需要 common_obj 中的 common,因此您可以从那里导出它(使用 Exporter 或仅添加命名空间)。所以你可以使用 $common_obj::done 或创建 &common_obj::done {return $done;} 并导出它。
  • 我接受了这个作为我的答案,因为它是第一个真正解决了我所说的问题的答案。谢谢!它不能解决我要解决的实际问题,但是通过您的解决方案,我已经缩小了范围,现在我可以问另一个更精确的问题,看看如何解决它。我会在这里发布一个链接,看看你们是否可以帮助我解决这个问题。再次感谢。
【解决方案3】:

很高兴您开始使用包,因为这将对您未来有很大帮助。为此,我建议您也开始重构旧代码。我可以理解不想碰任何旧的 cgi 文件,并且现在同意这个选择。但是您需要编辑一些包含的模块才能到达您想要的位置。

使用您的示例作为基线,目标是让 myfile.cgi 和所有类似的文件保持不变,但其他一切都是公平的游戏。

第 1 步 - 创建一个新包以包含 common.pm 中的函数和变量

common.pm 必须是一个包,但你不能这样做而不影响旧代码。解决方法是创建一个全新的包以包含旧文件中的所有函数和变量。这也是为所有当前和要创建的包创建更好的命名约定的好机会。我将假设您可能没有将其命名为 common.pm,但无论如何,您应该选择一个目录并命名您的项目。我将随机选择名称 MyProject::Core 用于之前保存在 common.pm 中的函数和变量。pm

package MyProject::Core;

@EXPORT = qw();
@EXPORT_OK = qw($done func_c);
%EXPORT_TAGS = (all => [@EXPORT, @EXPORT_OK]);

use strict;
use warnings;

our $done = "bye";

sub func_c {
    print @_, "\n";
}

1;

__END__

这个新包应该放在 MyProject/Core.pm 中。您需要在 EXPORT_OK 列表中包含所有要导出的变量和函数。另外,作为一个小说明,我在您的示例中的所有打印语句中都添加了返回字符,只是为了使测试更容易。

其次,编辑您的 common.pm 文件以仅包含以下内容:

use MyProject::Core qw(:all);

1;

__END__

你的 myfile.cgi 应该和现在一样工作。在继续之前确认这一点。

接下来,您可以开始创建新的包,这些包将依赖于旧 common.pm 中的函数和变量。您的示例 common_obj.pm 可以重新编码为以下内容:

package common_obj;

use MyProject::Core qw($done func_c);

use base Exporter;
@EXPORT_OK = qw(func_obj finish);

use strict;
use warnings;

sub func_obj {func_c(@_);}
sub finish {print "$done\n";}

1;

__END__

最后,myfile_obj.cgi 被重新编码为:

use common_obj qw(func_obj finish);

use strict;
use warnings;

func_obj('hi');
finish();

1;

__END__

现在,我可以使用@EXPORT 而不是@EXPORT_OK 来自动导出所有可用的函数和变量,但是最好只选择性地导入那些你真正需要的函数。此方法还使您的代码更具自文档性,因此查看任何文件的人都可以搜索以查看特定函数的来源。

希望这可以帮助您获得更好的编码实践。重构旧代码可能需要很长时间,但不断更新技能和工具绝对是一种值得的做法。

【讨论】:

  • 嗨,感谢您的出色回答,它确实帮助我了解了解决问题所需的知识。不幸的是,albe 提供了一个答案,解决了我首先提到的问题,所以我选择接受 albe 的答案。但是,通过您的回答和 albe 的回答,我已经能够缩小我的实际用例问题的范围,现在我知道它为什么不起作用,但不知道如何解决它,所以我将为此提出另一个问题。我会在这里发布链接。我希望我能为您的回答加分,但我没有足够的声誉。再次感谢。
  • 是的,了解 Exporter 是关键的第一步。祝你好运,弄清楚如何继续重构你的代码。我会密切关注您未来的问题。
猜你喜欢
  • 1970-01-01
  • 2019-09-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-10
  • 2016-06-21
  • 2014-09-13
相关资源
最近更新 更多