对我来说,Perl允许包将符号导出到另一个包的命名空间似乎很奇怪。导出包不知道using包是否已经使用相同的名称定义了符号,并且它当然不能保证它是唯一一个按该名称导出符号的包。
由此引起的very common problem同时使用CGI和LWP::Simple。两个包都导出head()
并导致错误。我知道,解决这个问题很容易,但这不是重点。您不应该使用工作来使用两个实际核心的Perl库。
据我所知,这样做的唯一原因是懒惰。通过不键入Foo ::或使用对象接口来保存一些击键,但是它真的值得吗?
答案 0 :(得分:9)
默认情况下,从模块导出所有函数的做法不是Perl推荐的做法。如果你有充分的理由,你应该只导出函数。建议的做法是使用EXPORT_OK
,以便用户必须键入所需功能的名称,例如:
use My::Module 'my_function';
像LWP :: Simple和CGI这样的模块在此推荐出现之前编写,现在很难将它们改为不导出东西,因为它会破坏现有的软件。我猜这个建议是通过人们注意到这样的问题来实现的。
无论如何,Perl的面向对象或其他任何东西都不需要你导出任何东西,你也不必说$ foo->,所以你的部分问题是错误的。
答案 1 :(得分:9)
导出是一项功能。与任何语言中的所有其他功能一样,如果您(ab)过于频繁地使用它,或者您不应该使用它,则可能会导致问题。如果明智地使用它会很好,否则就像其他任何功能一样。
在那个周围没有很多模块的那天,默认情况下输出东西似乎不是一件坏事。然而,在CPAN上有15,000个包,肯定会有冲突,这是不幸的。但是,现在修复模块可能会破坏现有代码。每当你做出糟糕的界面选择并将其发布给公众时,即使你不喜欢它,你仍然会致力于它。
所以,它很糟糕,但这就是它的方式,并且有很多方法。
答案 2 :(得分:6)
导出包不知道using包是否已经使用相同的名称定义了一个符号,并且它当然不能保证它是唯一一个按该名称导出符号的包。
如果你愿意,我想你的import()
例程可以检查,但默认的Exporter.pm例程不检查(也可能不应该,因为它会被大量使用,并且总是检查如果定义了名称会导致重大减速(如果发现冲突,那么Exporter期望做什么?)。)。
据我所知,这样做的唯一原因是懒惰。通过不键入Foo ::或使用对象接口来保存一些击键,但是它真的值得吗?
Foo::
并不是那么糟糕,但请考虑My::Company::Private::Special::Namespace::
- 如果我们只输出一些内容,您的代码会更清晰。并非所有内容都可以(或应该)位于顶级命名空间中。
在使代码更清晰时,应使用导出机制。当命名空间发生冲突时,不应该使用它,因为它显然不会使代码更清晰,但除此之外,我还是在请求时导出内容的粉丝。
答案 3 :(得分:3)
这不只是懒惰,而且不仅仅是旧模块。将Moose,“后现代对象系统”和Rose::DB::Object作为流行ORM的对象接口。两者都将meta
方法导入use
包的符号表,以便在该模块中提供功能。
与从每个提供相同名称的方法的模块中多次继承的问题没有什么不同,除了您的父母的顺序将决定调用该方法的哪个版本(或者您可以定义自己的方法)覆盖版本,以某种方式手动折叠父母双方的特征)。
就我个人而言,我希望能够将Rose :: DB :: Object与Moose结合起来,但是解决这个问题并不是什么大不了的事:人们可以制作一个Moose派生类“有一个”Rose ::其中包含DB :: Object派生对象,而不是“是一个”(即继承自)Rose :: DB :: Object。
答案 4 :(得分:3)
关于Perl的“开放”软件包的一个美好的事情是,如果你对模块作者设计某种东西的方式并不感到疯狂,你可以改变它。
package LWPS;
require LWP::Simple;
for my $sub (@LWP::Simple::EXPORT, @LWP::Simple::EXPORT_OK) {
no strict 'refs';
*$sub = sub {shift; goto &{'LWP::Simple::' . $sub}};
}
package main;
my $page = LWPS->get('http://...');
当然,在这种情况下,LWP::Simple::get()
可能会更好。