提问人:U. Windl 提问时间:7/28/2023 最后编辑:U. Windl 更新时间:7/30/2023 访问量:46
Perl多重继承,两次继承相同的方法名:如何正确使用正确的方法?
Perl multiple inheritance, inheriting the same method name twice: How to use the right method correctly?
问:
好吧,我不应该使用使用多重继承的Perl,但我做到了。
请考虑以下代码草图(表示当前对象):$self
package A;
sub f { ... }
sub _init { ...; $self->f; ... }
sub new { ... $self->_init ... }
package B;
sub _init { ... }
sub new { ... $self->_init ... }
package C;
sub f { ... }
our @ISA = qw(A B);
sub new
{
my $class = $_[0];
my $self = ...;
bless $self, $class;
??? call A:_init using $self, call B:_init using $self, setup C's attributes
}
面临的挑战是如何集成 from 和 的构造函数(假设完成除祝福和分配对象之外的所有设置)。
using 只能使用两个继承方法中的一个,并且不是很明显(可能来自第一个列出的包)。C->new
A
B
_init
$self->SUPER::_init
@ISA
另一件事是如何确保使用动态绑定,即:使用?具体使用不。
具体来说也可以有自己的,甚至可以继承更多的包......A::_init
f
C::f
A::_init($self)
C
_init
A
B
可编译示例
下面是一个编译的代码示例,它演示了应该发生什么(注释的语句已经来自 https://stackoverflow.com/a/76786760/6607497):
#!/usr/bin/perl
require 5.018_000;
use strict;
use warnings;
package P1;
sub f($)
{
$_[0]->[0] = 1;
}
sub _init($)
{
$_[0]->f();
}
sub new($)
{
my $class = $_[0];
my $self = [];
$#$self = 0;
bless $self, $class;
$self->_init();
return $self;
}
package P2;
sub _init($)
{
$_[0]->[0] = 2;
}
sub new($)
{
my $class = $_[0];
my $self = [];
$#$self = 0;
bless $self, $class;
_self->_init();
return $self;
}
package P3;
our @ISA = qw(P1 P2);
BEGIN {
use Exporter qw(import);
}
sub f($)
{
$_[0]->[1] = 3;
}
sub _init($)
{
$_[0]->[2] = 4;
}
sub new($)
{
my $class = $_[0];
my $self = [];
$#$self = 2;
bless $self, $class;
#$self->P1::_init();
#$self->P2::_init();
$self->_init();
return $self;
}
package min;
my $o = P3->new();
$DB::single = 1;
print $o, "\n"; # prevent optimizing $o away too early
所以当 sets , sets , so 's 时,当从对象调用时,不应该设置 ,而是设置 。P1::f
$self->[0] = 1
P3::f
$self->[1] = 3
P1
_init
P3
$self->[0] = 1
$self->[1] = 3
所以结果应该是:
DB<1> x $o
0 P3=ARRAY(0xbd27e8)
0 2
1 3
2 4
答:
子类可以执行以下操作:
package C;
our @ISA = qw(A B);
sub new {
my $class = $_[0];
my $self = ...;
bless $self, $class;
$self->A::_init();
$self->B::_init();
return $self;
}
如果 C 有自己的方法,我会这样写:_init
package C;
our @ISA = qw(A B);
sub new {
my $class = $_[0];
my $self = ...;
bless $self, $class;
$self->_init;
return $self;
}
sub _init {
my $self = shift;
$self->A::_init();
$self->B::_init();
...; # more initialization
}
您关心的问题之一似乎是确保使用动态绑定。如果使用箭头运算符 () 调用方法,它将始终使用动态绑定。$object->f
->
因此,如果调用(静态绑定)但在调用中发生,则该调用将使用动态绑定。A::_init($object)
A::_init
$self->f
所以在我的例子中,为什么我用 instead of ?因为前一种语法使用动态绑定,所以它只是从“A”开始方法解析,而不是从“C”开始。$self->A::_init()
A::_init($self)
一个更复杂的例子:
package Grandparent {
use Class::Tiny; # provides `new`
sub init {
my $self = shift;
print "From Grandparent\n";
}
}
package Parent1 {
use parent -norequire, 'Grandparent'; # provides `@ISA`
}
package Parent2 {
use Class::Tiny;
sub init {
my $self = shift;
print "From Parent2\n";
}
}
package Child {
use parent -norequire, 'Parent1', 'Parent2';
sub init {
my $self = shift;
$self->Parent1::init();
$self->Parent2::init();
print "From Child\n";
}
}
my $eg = Child->new;
$eg->init;
注意:在此示例中,继承自两个父类。它的方法通过其每个父级调用该方法,包括未定义自己的 init
方法但继承自 的类。Child
init
init
Parent1
Grandparent
评论
虽然可以在类层次结构中显式调用不同的方法,但这似乎是一种反模式。如果层次结构中的某个位置发生更改,则需要调整其他位置的代码,以保持程序按预期工作。init()
@ISA
Perl 在 core 中有一个叫做 mro 的模块,它提供了在 dfs(默认)和 c3 之间切换类方法解析顺序的可能性。 即使你想将 mro 保留在 dfs,你也可以使用 next::method() 和 maybe::next method() 它们总是使用 c3 mro 来解析方法调用。
下面的示例添加一个公共父类,其中包含按正确顺序调用的可继承构造函数和 init 方法。请注意,通常首先让 init 代码在父类中完成工作。
use strict;
use warnings;
use feature 'say';
use mro;
package GP;
sub new($)
{
my $class = $_[0];
my $self = [];
bless $self, $class;
$self->_init();
return $self;
}
sub _init
{
say 'GP::init called';
say 'GP::init: do work';
}
package P1;
our @ISA = 'GP';
sub f($)
{
say 'P1::f called';
}
sub _init($)
{
my $self = shift;
say 'P1::init called';
$self->maybe::next::method(@_);
say 'P1::init: do work, call ->f()';
$self->f();
}
package P2;
our @ISA = 'GP';
sub _init($)
{
my $self = shift;
say 'P2::init called';
$self->maybe::next::method(@_);
say 'P2::init: do work';
}
package P3;
our @ISA = qw(P1 P2);
sub f($)
{
say 'P3::f called';
}
sub _init($)
{
my $self = shift;
say 'P3::init called';
$self->maybe::next::method(@_);
say 'P3::init: do work';
}
package main;
my $o = P3->new;
# P3::init called
# P1::init called
# P2::init called
# GP::init called
# GP::init: do work
# P2::init: do work
# P1::init: do work, call ->f()
# P3::f called
# P3::init: do work
评论
A
B
C