如何动态加载Perl模块并使用它们的函数?

如何动态加载Perl模块并使用它们的函数?,perl,Perl,我正在尝试将代码中的一些use语句切换为require,以便仅在满足某些条件时使用这些模块(例如,在需要这些模块的时间之间,程序可能加载1000次),以降低开销并提高程序的执行速度(我一直在使用Devel::NYTProf评测我的代码,一些模块甚至在我没有在特定会话中使用它们的情况下也增加了大量的执行时间) 我遇到了一个问题,脚本在加载时死亡,因为没有定义符号(在导入该模块之前不会定义符号)。有没有办法告诉Perl符号将在运行时导入?一个简单的示例:如果调用触发它的例程,我只想使用Data::D

我正在尝试将代码中的一些
use
语句切换为
require
,以便仅在满足某些条件时使用这些模块(例如,在需要这些模块的时间之间,程序可能加载1000次),以降低开销并提高程序的执行速度(我一直在使用Devel::NYTProf评测我的代码,一些模块甚至在我没有在特定会话中使用它们的情况下也增加了大量的执行时间)

我遇到了一个问题,脚本在加载时死亡,因为没有定义符号(在导入该模块之前不会定义符号)。有没有办法告诉Perl符号将在运行时导入?一个简单的示例:如果调用触发它的例程,我只想使用Data::Dumper:

sub dataDumper {
    require Data::Dumper;
    Data::Dumper->import;
    say STDERR Dumper @_;
}
但是,在第三行调用
Dumper
会导致异常。我可以通过引用
Data::Dumper->Dump
来解决这个问题。然而,LWP::UserAgent的类似问题证明了这一点,因为LWP::UserAgent导入了HTTP::Request。我还没有找到
my$Request=new HTTP::Request G的变体ET=>$params->{'url'};
避免引发异常


假设我能做到这一点,有什么原因说明这种方法是个坏主意吗?我是否因为没有在编译时加载这些模块而失去了任何优化?在LWP这样的模块上,这似乎是一个简单的调用——我只是不太使用它。我对是否尝试在两者之间进行动态切换(例如)有点困惑Text::Textile和Text::MultiMarkdown(我通常每次执行只需要一个。

使用完全限定名,不导入任何内容

子数据转储程序{
需要数据::转储程序;
说STDERR Data::Dumper::Dumper(@);
}
然而,LWP::UserAgent的类似问题证明了这一点,因为LWP::UserAgent导入HTTP::Request

此语句中的术语是错误的。LWP::UserAgent不导入任何内容。事实上,它根本没有
import
方法。它通过
use
ing加载HTTP::Request。因此,perl将在LWP::UA为
require
d时切换到编译时,编译它,并加载HTTP::Request、HTTP::Response和HTTP::Date(和其他一些),也可以放入LWP::UserAgent的命名空间中

如果HTTP::Request没有被加载,我就无法重现这个爆炸。如果它没有被执行,它就不会出错

if(0){
HTTP::Request->new(GET=>'/foo');
新的HTTP::Request GET=>'/foo';
}
这很好。你当然应该使用第一种形式,而不是间接对象表示法,因为它是不明确的,但没有区别

如果要使用,它导入
GET
POST
等以快速生成HTTP::请求,那么您也可以在运行时
要求它,然后使用完全限定的名称

if($make\u web\u请求){
需要LWP::UserAgent;
需要HTTP::Request::Common;
my$ua=LWP::UserAgent->new;
$ua->request(HTTP::request::Common::GET('https://example.org'));
}
我是否因为没有在编译时加载这些模块而失去了某种优化


不需要。如果你的代码启动了很多次,并且走了很多不同的路径,那么只在你需要的时候加载就完全可以了。这几乎没有什么真正的区别。Perl会在编译时和运行时之间为每一个
require
进行切换。通常这根本不是一个惩罚。

在子调用中,parens around参数只能为已声明的sub省略

say STDERR Dumper(@);
请注意,以这种方式加载的SUB上的原型将被忽略


第二行没有问题,因为它不涉及子调用。方法调用不受影响

$perl-M5.010-e'
如果($ARGV[0]){
需要LWP::UserAgent;#或HTTP::Request
我的$request=newhttp::request GET=>“https://stackoverflow.com";
}
说“好”;
' 0
好啊
$perl-M5.010-e'
如果($ARGV[0]){
需要LWP::UserAgent;#或HTTP::Request
我的$request=newhttp::request GET=>“https://stackoverflow.com";
}
说“好”;
' 1
好啊

这样,类模块在这里特别有用。考虑复杂情况下的插件模型。


我是否因为没有在编译时加载这些模块而失去了某种优化

常数需要替换为不能折叠的较慢的函数调用

$perl-MO=简明,-exec-M5.010-e'
使用常量FOO=>2;
说2+FOO;
'
...
下一状态(主188-e:3)v:{,fea=1
3个pushmark s
4常数[IV 4]s/倍
5说vK
...
$perl-MO=简明,-exec-M5.010-e'
说2+FOO();
需要常量;导入常量FOO=>2;
'
...
2下一状态(主2-e:2)v:{,fea=1
3个pushmark s
4常数[IV 2]s
5个pushmark s
6 gv[*FOO]s/EARLYCV
7分公司[t2]sKS/TARG
8添加[t3]sK/2
9说vK
...

我做了类似的事情,但像这样。不知何故,
状态
使这一过程变得更快,尽管
要求
不会再次加载它:

sub something {
    state $rc = require Foo;
    ...
    }
如果我想进口东西

sub something {
    state $rc = do { require Foo; Foo->import };
    ...
    }
记住
使用
实际上是:

BEGIN {
    require Foo;
    Foo->import(...);
    }

BEGIN
仅在编译时执行此操作。如果您稍后执行所有相同的步骤,您将在相同的位置(稍后)结束。

Re“使用完全限定名,不导入任何内容。”,这并不是造成差异的原因;这是对paren的添加。@simbabque谢谢!这似乎解决了我的问题——感谢关于HTTP::Request的提示!如果启动时间如此繁重,可能值得研究一个持久性服务(就像FCGI对CGI一样)@ikegami这听起来很有趣。有没有一个好的指南可以推荐你使用一个现有的程序并用一个持久的系统来实现它?听起来PSGI是今天最好的方法?对不起,没有…。谢谢你!你又来了
BEGIN {
    require Foo;
    Foo->import(...);
    }