我正在开发一个程序,该程序进行了多次尝试,每次尝试时都会将其存储到新日志中(之前/之后的其他几个步骤)。
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()调用可靠并使用正确对象的其他建议。
答案 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现在是可以在此函数之外访问的全局变量。