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

Perl 使用对象作为常量有什么问题吗?,perl,Perl,我使用了一个WWW::Mechanize对象作为常量,它对我来说运行良好,但感觉有点奇怪 use constant MECH => WWW::Mechanize->new(agent => 'my_app'); 使用对象作为常量有什么“错误”吗?没有,不是天生的错误 Perl常量只是一个具有空原型的内联子例程。在您提供的示例中,可以将MECH视为WWW::Mechanize类实例的访问器。同一实例将与MECH常量的任何使用者共享 这: …大致相当于以下代码段: BEGIN {

我使用了一个
WWW::Mechanize
对象作为常量,它对我来说运行良好,但感觉有点奇怪

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

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

没有,不是天生的错误

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

这:

…大致相当于以下代码段:

BEGIN {
    my $mech;
    sub MECH() {
        return $mech //= WWW::Mechanize->new(agent => 'my_app');
    }
}
或者,使用
状态
变量:

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

不,不是天生的错误

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

这:

…大致相当于以下代码段:

BEGIN {
    my $mech;
    sub MECH() {
        return $mech //= WWW::Mechanize->new(agent => 'my_app');
    }
}
或者,使用
状态
变量:

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

它基本上是好的,但它只是一个常数。我经常使用常量URL,如下所示

use constant URL => URI->new('http://www.example.com/');
但是如果你真的在修改对象,那么它可能会有点混乱。我从常量中克隆以创建类似的值,如

my $url = URL->clone->path('/path/to/resource');
如果您希望使用类似的方法,那么
WWW::Mechanize
也有一个
clone
方法。例如,我认为把它用作变量是不合适的

MECH->get($url);

我不明白你为什么想

基本上没问题,但只有参考是恒定的。我经常使用常量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中使用
use constant
定义的所谓常量的一个好处是启用常量折叠。例如,当您说
use constant N_CONNECTIONS=>5
时,
perl
在任何地方看到
N_CONNECTIONS
,它在编译时被替换为
5
,使进一步优化成为可能

$perl -MO=Deparse -e 'use constant X => 17; if ( X < 5 ) { rand }'
use constant ('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
并且您不需要传递它

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


如果你不同意我的意见,就用吧。

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

$perl -MO=Deparse -e 'use constant X => 17; if ( X < 5 ) { rand }'
use constant ('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
,您不需要传递它

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


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

不,它实际上接近于
开始{my$mech=WWW::Mechanize->new(agent=>'my_app');sub-mech(){$mech}
。初始化没有延迟。是的,在其他应用程序中为此我使用了单例(显然是利用
state
)。基本上,我正在用不同的方法使全局变量变得优雅:PNo,它实际上接近于
BEGIN{my$mech=WWW::Mechanize->new(agent=>'my_app');sub-mech(){$mech}
。发炎