如何确保我的方法调用在正确的对象上使用正确的方法名称?

时间:2020-06-12 13:25:57

标签: perl methods

我正在开发一个程序,该程序进行了多次尝试,每次尝试时都会将其存储到新日志中(之前/之后的其他几个步骤)。

use strict;
for (my $i = 0; $i < 3; $i++)
{
     my $loggerObject = new MyLoggerObject(tag => $i);
     #.. do a bunch of other things ..
     Process($loggerObject,$i);
     #.. do a bunch of other things ..
}
sub Process
{

    my ($logger,$thingToLog) = @_;
    sub Logger { $logger->Print($_[0]); }
    Logger("Processing $thingToLog");

}



package MyLoggerObject;

sub new
{
    my $package = shift;
    my %hash = (@_); my $self = \%hash;
    return bless $self, $package;
}

sub Print
{
   my $self = shift;
   my $value = shift;
   print "Entering into log ".$self->{tag}.": $value\n";
}

1;

为避免不得不执行大量$ self-> {logger}-> Print()并冒错拼写Print的风险,我试图将它们折叠到本地子例程中,如上所示。但是,当我运行它时,我得到:

perl PerlLocalMethod.pl 
Entering into log 0: Processing 0
Entering into log 0: Processing 1
Entering into log 0: Processing 2

代替:

perl PerlLocalMethod.pl 
Entering into log 0: Processing 0
Entering into log 1: Processing 1
Entering into log 1: Processing 2

我认为问题在于,第一次使用第一次调用时使用的对象引用调用Logger方法是“编译”的,但之后没有使用。 如果我执行了$ logger-> Print(),Print拼写错误并且遇到了我无法可靠测试的代码路径(这是针对嵌入式系统的,我无法强制执行所有错误条件),那么它将以未定义的错误提示出脚本方法。我想我可以在记录器中使用AUTOLOAD并记录任何错误的Method调用,但是我想知道有关如何确保Logger()调用可靠并使用正确对象的其他建议。

3 个答案:

答案 0 :(得分:4)

在Perl中,子例程在编译时进行编译。将命名的子例程声明嵌入到子例程中并不能达到预期效果,因此不建议这样做。

如果您担心输入错误,请编写测试。有关如何操作,请参见Test::More。如果无法在开发机器上实例化系统特定的类,请使用模拟。或使用较短的名称,例如P

您可以将Logger声明为最高范围,作为$ logger的闭包,您也需要在其中声明:

my $logger;
sub Logger { $logger->Print($_[0]) }

但是,如果有很多这样的变量和子例程,它会造成混乱并导致代码难以维护。

答案 1 :(得分:4)

如果您在代码中使用use warnings,则会看到以下消息:

变量“ $ logger”将不会在记录器的第24行保持共享状态。

可能会警告您该问题(道德:始终 use strict use warnings)。

我不完全确定为什么要进行日志记录需要这么多级别的子例程,但是在我看来,所有将$logger对象作为其第一个参数的子例程应该MyLoggerObject上的方法(由于它是一个类,而不是对象,因此应称为MyLoggerClass)。

如果您这样做,那么您最终将获得以下代码(它似乎可以满足您的要求):

use strict;
use warnings;

for my $i (0 .. 2) {
    my $loggerObject = MyLoggerClass->new(tag => $i);
    #.. do a bunch of other things ..
    $loggerObject->Process($i);
    #.. do a bunch of other things ..
}



package MyLoggerClass;

sub new {
    my $package = shift;
    my $self = { @_ };
    return bless $self, $package;
}

sub Process {
    my $self = shift;
    my ($thingToLog) = @_;

    $self->Logger("Processing $thingToLog");
}

sub Logger {
    my $self = shift;

    $self->Print($_[0]);
}

sub Print {
   my $self = shift;
   my ($value) = @_;

   print "Entering into log $self->{tag}: $value\n";
}

1;

哦,请注意,我从间接对象表示法调用(new Class(...))移到了更安全的Class->new(...)。您使用的样式在大多数情况下都可以使用,但是如果不行,您将浪费大量时间来尝试解决问题。

答案 2 :(得分:0)

如上所述,在此类方法中无法使用词法定义的变量。

如果您必须“引导”这个问题,则可以使用全局变量(我们代替我的)。

class LoginSpider(scrapy.Spider):
    name = 'wine'
    start_urls=['https://www.jancisrobinson.com/#login']

    def parse(self, response):
        return scrapy.FormRequest.from_response(
            response,
            formdata={'username': 'cathy@enolytics.com', 'password': 'purple'},
            callback=self.after_login)

    def after_login(self, response):
        if authentication_failed(response):
            self.logger.error("Login failed")
            return
        else:
            self.logger.error("Login succeeded!")
            item = SampleItem()
            item["quote"] = response.css(".text").extract()
            item["author"] = response.css(".author").extract()
            return item

    def start_requests(self):
        return [scrapy.FormRequest("https://www.jancisrobinson.com/#login",
                                   formdata={'user': 'john', 'pass': 'secret'},
                                   callback=self.logged_in)]

    def logged_in(self,reponse):
        # do something here
        pass

但是请注意,$ logger和$ thingToLog现在是可以在此函数之外访问的全局变量。