观察 Perl 中属性的变化 class
Watch change of attribute inside Perl class
任何人都可以提供一个代码示例,您如何在 class 内部的变量更改上设置观察者?我尝试使用不同的功能 (Scalar::Watcher, trigger attribute of Moo) 和 OOP 框架 (Moo, Mojo::Base) 以多种方式完成它,但都失败了。
为了更好地理解我的任务,下面是我失败的代码。在此示例中,每次 attr1 更改时我都需要更新 attr2。
使用 Mojo::Base 和 Scalar::Watcher:
package Cat;
use Mojo::Base -base;
use Scalar::Watcher qw(when_modified);
use feature 'say';
has 'attr1' => 1;
has 'attr2' => 2;
has 'test' => sub { # "fake" attribute for getting access to $self
my $self = shift;
when_modified $self->attr1, sub { $self->attr2(3); say "meow" };
};
package main;
use Data::Dumper;
my $me = Cat->new;
$me->attr1;
warn Dumper $me;
say $me->attr1(3)->attr2; # attr2 is still 2, but must be 3
使用 Moo 和触发器:
package Cat;
use Moo;
use Scalar::Watcher qw(when_modified);
use feature 'say';
has 'attr1' => ( is => 'rw', default => 1, trigger => &update() );
has 'attr2' => ( is => 'rw', default => 1);
sub update {
my $self = shift;
when_modified $self->attr1, sub { $self->attr2(3); say "meow" }; # got error here: Can't call method "attr1" on an undefined value
};
package main;
use Data::Dumper;
my $me = Cat->new;
$me->attr1;
warn Dumper $me;
say $me->attr1(3)->attr2;
非常感谢任何建议。
Moo 部分
got error here: Can't call method "attr1" on an undefined value
这是因为 Moo 期望 has
的代码引用为 trigger
。您正在将调用结果传递给 update
。这里的 &
并没有给你一个引用,而是告诉 Perl 忽略 update
函数的原型。你不想要那个。
相反,使用 \&foo
创建引用并且不添加括号 ()
。你不想调用这个函数,你想引用它。
has 'attr1' => ( is => 'rw', default => 1, trigger => \&update );
一旦您完成了该操作,您就不再需要 Scalar::Watcher 了。触发器已经做到了。每次 attr1
更改时都会调用它。
sub update {
my $self = shift;
$self->attr2(3);
say "meow";
};
如果你现在 运行 整个事情,它会工作一点,但崩溃并出现此错误:
Can't locate object method "attr2" via package "3" (perhaps you forgot to load "3"?) at
那是因为 attr1
return 是新值,而不是对 $self
的引用。所有 Moo/Moose 访问器都是这样工作的。 3
不是对象,所以它没有方法 attr2
# this returns 1
# |
# V
say $me->attr1(3)->attr2;
而是分两次调用。
$me->attr1(3);
say $me->attr2;
这是一个完整的例子。
package Cat;
use Moo;
use feature 'say';
has 'attr1' => ( is => 'rw', default => 1, trigger => \&update );
has 'attr2' => ( is => 'rw', default => 1 );
sub update {
my $self = shift;
$self->attr2(3);
say "meow";
}
package main;
my $me = Cat->new;
say $me->attr2;
$me->attr1(3);
say $me->attr2;
并且输出:
1
meow
3
为什么 Scalar::Watcher 不适用于 Mojo
首先,Mojo::Base没有提供触发机制。但是您实现 Scalar::Watcher 的方式无法工作,因为从未调用过 test
方法。我尝试在基于 Mojo::Base 的 class 中挂钩 new
以在始终被调用的地方进行 when_modified
调用。
这里的一切都只是猜测。
以下代码段是我尝试过的,但它不起作用。我将在下面进一步解释原因。
package Cat;
use Mojo::Base -base;
use Scalar::Watcher qw(when_modified);
use feature 'say';
has 'attr1' => '1';
has 'attr2' => 'original';
sub new {
my $class = shift;
my $self = $class->SUPER::new(@_);
when_modified $self->{attr1}, sub { $self->attr2('updated'); say "meow" };
return $self;
}
如您所见,这现在是 new
调用的一部分。代码确实得到执行。但是没用。
documentation of Scalar::Watcher states 观察者应该在那里直到变量超出范围。
If when_modified is invoked at void context, the watcher will be
active until the end of $variable's life; otherwise, it'll return a
reference to a canceller, to cancel this watcher when the canceller is
garbage collected.
但实际上我们没有标量变量。如果我们尝试做
when_modified $self->foo
然后 Perl 在 $self
上执行 foo
的方法调用,when_modified
将获得该调用的 return 值。我也尝试进入上面对象的内部结构,但这也没有用。
我的 XS 不够强大,无法理解正在发生的事情 here,但我认为它在附加魔法时遇到了一些问题。它不能与哈希引用值一起使用。可能这就是为什么它被称为 Scalar::Watch.
任何人都可以提供一个代码示例,您如何在 class 内部的变量更改上设置观察者?我尝试使用不同的功能 (Scalar::Watcher, trigger attribute of Moo) 和 OOP 框架 (Moo, Mojo::Base) 以多种方式完成它,但都失败了。
为了更好地理解我的任务,下面是我失败的代码。在此示例中,每次 attr1 更改时我都需要更新 attr2。
使用 Mojo::Base 和 Scalar::Watcher:
package Cat;
use Mojo::Base -base;
use Scalar::Watcher qw(when_modified);
use feature 'say';
has 'attr1' => 1;
has 'attr2' => 2;
has 'test' => sub { # "fake" attribute for getting access to $self
my $self = shift;
when_modified $self->attr1, sub { $self->attr2(3); say "meow" };
};
package main;
use Data::Dumper;
my $me = Cat->new;
$me->attr1;
warn Dumper $me;
say $me->attr1(3)->attr2; # attr2 is still 2, but must be 3
使用 Moo 和触发器:
package Cat;
use Moo;
use Scalar::Watcher qw(when_modified);
use feature 'say';
has 'attr1' => ( is => 'rw', default => 1, trigger => &update() );
has 'attr2' => ( is => 'rw', default => 1);
sub update {
my $self = shift;
when_modified $self->attr1, sub { $self->attr2(3); say "meow" }; # got error here: Can't call method "attr1" on an undefined value
};
package main;
use Data::Dumper;
my $me = Cat->new;
$me->attr1;
warn Dumper $me;
say $me->attr1(3)->attr2;
非常感谢任何建议。
Moo 部分
got error here: Can't call method "attr1" on an undefined value
这是因为 Moo 期望 has
的代码引用为 trigger
。您正在将调用结果传递给 update
。这里的 &
并没有给你一个引用,而是告诉 Perl 忽略 update
函数的原型。你不想要那个。
相反,使用 \&foo
创建引用并且不添加括号 ()
。你不想调用这个函数,你想引用它。
has 'attr1' => ( is => 'rw', default => 1, trigger => \&update );
一旦您完成了该操作,您就不再需要 Scalar::Watcher 了。触发器已经做到了。每次 attr1
更改时都会调用它。
sub update {
my $self = shift;
$self->attr2(3);
say "meow";
};
如果你现在 运行 整个事情,它会工作一点,但崩溃并出现此错误:
Can't locate object method "attr2" via package "3" (perhaps you forgot to load "3"?) at
那是因为 attr1
return 是新值,而不是对 $self
的引用。所有 Moo/Moose 访问器都是这样工作的。 3
不是对象,所以它没有方法 attr2
# this returns 1
# |
# V
say $me->attr1(3)->attr2;
而是分两次调用。
$me->attr1(3);
say $me->attr2;
这是一个完整的例子。
package Cat;
use Moo;
use feature 'say';
has 'attr1' => ( is => 'rw', default => 1, trigger => \&update );
has 'attr2' => ( is => 'rw', default => 1 );
sub update {
my $self = shift;
$self->attr2(3);
say "meow";
}
package main;
my $me = Cat->new;
say $me->attr2;
$me->attr1(3);
say $me->attr2;
并且输出:
1
meow
3
为什么 Scalar::Watcher 不适用于 Mojo
首先,Mojo::Base没有提供触发机制。但是您实现 Scalar::Watcher 的方式无法工作,因为从未调用过 test
方法。我尝试在基于 Mojo::Base 的 class 中挂钩 new
以在始终被调用的地方进行 when_modified
调用。
这里的一切都只是猜测。
以下代码段是我尝试过的,但它不起作用。我将在下面进一步解释原因。
package Cat;
use Mojo::Base -base;
use Scalar::Watcher qw(when_modified);
use feature 'say';
has 'attr1' => '1';
has 'attr2' => 'original';
sub new {
my $class = shift;
my $self = $class->SUPER::new(@_);
when_modified $self->{attr1}, sub { $self->attr2('updated'); say "meow" };
return $self;
}
如您所见,这现在是 new
调用的一部分。代码确实得到执行。但是没用。
documentation of Scalar::Watcher states 观察者应该在那里直到变量超出范围。
If when_modified is invoked at void context, the watcher will be active until the end of $variable's life; otherwise, it'll return a reference to a canceller, to cancel this watcher when the canceller is garbage collected.
但实际上我们没有标量变量。如果我们尝试做
when_modified $self->foo
然后 Perl 在 $self
上执行 foo
的方法调用,when_modified
将获得该调用的 return 值。我也尝试进入上面对象的内部结构,但这也没有用。
我的 XS 不够强大,无法理解正在发生的事情 here,但我认为它在附加魔法时遇到了一些问题。它不能与哈希引用值一起使用。可能这就是为什么它被称为 Scalar::Watch.