使用对象作为常量有什么问题吗?

  • 本文关键字:问题 常量 对象 perl
  • 更新时间 :
  • 英文 :


我正在使用一个WWW::Mechanize对象作为常量,它对我来说效果很好,但感觉有点奇怪。

use constant MECH => WWW::Mechanize->new(agent => 'my_app');

使用对象作为常量有什么"错误"吗?

基本上没问题,但只是引用是常量。我经常使用常量 URL,如下所示

use constant URL => URI->new('http://www.example.com/');

但是,如果您实际上正在修改对象,那么它可能会有点混乱。我从常量克隆以创建类似的值,例如

my $url = URL->clone->path('/path/to/resource');

如果您希望使用类似的方法,WWW::Mechanize也有clone方法。例如,我认为将其用作变量是不合适的

MECH->get($url);

我不明白你为什么要

不,本质上不是错的。

Perl 常量只是一个具有空原型的可内联子例程。在您提供的示例中,您可以将MECH视为WWW::Mechanize类实例的访问器。 同一实例将与MECH常量的任何使用者共享。

这:

use constant MECH => WWW::Mechanize->new(agent => 'my_app');

。大致相当于以下代码片段:

BEGIN {
my $mech;
sub MECH() {
return $mech //= WWW::Mechanize->new(agent => 'my_app');
}
}

或者,使用state变量:

sub MECH() {
state $mech = WWW::Mechanize->new(agent => 'my_app');
return $mech;
}

我的建议是不要这样做。在 Perl 中使用use constant定义的所谓常量的好处是启用常量折叠。例如,当你说use constant N_CONNECTIONS => 5perl看到N_CONNECTIONS的任何地方,它在编译时被替换为5,使进一步的优化成为可能。

$perl -MO=Deparse -e 'use constant X => 17; if ( X < 5 ) { rand }'
use constant ('X', 17);
'???';
-e syntax OK

看,一大堆代码刚刚被修剪了。或

$ perl -MO=Deparse -e 'use constant X => 17; $x = X'
use constant ('X', 17);
$x = 17;
-e syntax OK

perl知道在编译时将文字17分配给$x

让我们看看您的构造的计算结果:

$ perl -MWWW::Mechanize -MO=Deparse -e 'use constant MECH => WWW::Mechanize->new(agent => "my_app"); $x = MECH'
use constant ('MECH', 'WWW::Mechanize'->new('agent', 'my_app'));
$x = {autocheck => 1, cookie_jar => {COOKIES => {}}, current_form => undef, def_headers => {user-agent => 'my_app'}, forms => undef, handlers => {request_prepare => [{callback => sub {
package LWP::UserAgent;
use strict;
$jar->add_cookie_header($_[0]);
}
, line => '/Users/.../opt/lib/perl5/site_perl/5.24.0/LWP/UserAgent.pm:755', owner => 'LWP::UserAgent::cookie_jar'}], response_done => [{callback => sub {
package LWP::UserAgent;
use strict;
$jar->extract_cookies($_[0]);
}
, line => '/Users/.../opt/lib/perl5/site_perl/5.24.0/LWP/UserAgent.pm:758', owner => 'LWP::UserAgent::cookie_jar'}], response_header => [{callback => sub {
package LWP::UserAgent;
use strict;
my($response, $ua) = @_;
require HTML::HeadParser;
$parser = 'HTML::HeadParser'->new;
$parser->xml_mode(1) if $response->content_is_xhtml;
$parser->utf8_mode(1) if $] >= 5.008 and $HTML::Parser::VERSION >= 3.4;
push @{$$response{'handlers'}{'response_data'};}, {'callback', sub {
return unless $parser;
unless ($parser->parse($_[3])) {
my $h = $parser->header;
my $r = $_[0];
foreach my $f ($h->header_field_names) {
$r->init_header($f, [$h->header($f)]);
}
undef $parser;
}
}
};
}
, line => '/Users/.../opt/lib/perl5/site_perl/5.24.0/LWP/UserAgent.pm:734', m_media_type => 'html', owner => 'LWP::UserAgent::parse_head'}]}, headers => {}, images => undef, links => undef, local_address => undef, max_redirect => 7, max_size => undef, no_proxy => [()], noproxy => 0, onerror => sub {
package WWW::Mechanize;
use warnings;
use strict;
require Carp;
return &Carp::croak;
}
, onwarn => sub {
package WWW::Mechanize;
use warnings;
use strict;
require Carp;
return &Carp::carp;
}
, page_stack => [()], protocols_allowed => undef, protocols_forbidden => undef, proxy => {}, quiet => 0, requests_redirectable => ['GET', 'HEAD', 'POST'], show_progress => undef, ssl_opts => {verify_hostname => 1}, stack_depth => 8675309, text => undef, timeout => 180, title => undef, use_eval => 1};
-e syntax OK

所以,无论你在哪里使用MECH,它都会在编译时被这个粗糙的东西所取代。

当然,事情会奏效的,但你会故意写这样的东西吗?

由于MECH现在是包中的一个子例程,因此可以通过外部代码调用它。这对您来说可能无关紧要,但它确实会破坏封装。

现在,我们来谈谈我的另一个问题。实际上,WWW::Mechanize的实例不是一个常量。每次对其调用方法时,其内部状态都会发生变化。因此,使用常量来指代处于某种状态的东西充其量似乎是不协调的。

看起来您正在尝试避免将$mech传递给一堆子例程。这是将特定爬网程序封装在类中并为其提供mech类成员的好方法。然后,类中的方法可以将其称为$self->mech,而不需要传递它。

但是,最终,你问了一个主观问题。我试图解释为什么这是一个好主意。一般来说,使用全局变量是一个坏主意。

如果您不同意我的观点,请继续使用它。

最新更新