为啥perl对象实例会互相覆盖

Posted

技术标签:

【中文标题】为啥perl对象实例会互相覆盖【英文标题】:Why does perl object instance overwrite each other为什么perl对象实例会互相覆盖 【发布时间】:2013-07-02 14:33:21 【问题描述】:

我已经编写了一些 Perl 代码,这些代码由基础类所固有的两个类组成。我想它会打印出这样的东西

Mik: Meow! Meow!
Sat: Woof! Woof!

但它实际上是这样打印的:

Sat: Woof! Woof!
Sat: Woof! Woof!

,

package Animal;
sub new 

    my $obj = shift;
    my $name = shift;
    our %pkg = ( 'name' => $name );
    bless \%pkg, $obj;
    return \%pkg;


package Cat;
@ISA = ("Animal");

sub new 
    my $obj = shift;
    my $name = shift;
    my $self =  $obj->SUPER::new($name);
    return $self;


sub get_name 
    my $obj = shift;
    return $obj->'name';



sub talk 
    my $obj = shift;
    return "Meow! Meow!";


package Dog;
@ISA = ("Animal");

sub new 
    my $obj = shift;
    my $name = shift;
    my $self = $obj->SUPER::new( $name );
    return $self;


sub get_name 
    my $obj = shift;
    return $obj->'name';


sub talk 
    my $obj = shift;
    return "Woof! Woof!";


package Main;

my $cat = new Cat('Mike');
my $dog = new Dog('Sat');

print $cat->get_name() . ": " . $cat->talk() , "\n"; 
print $dog->get_name() . ": " . $dog->talk() , "\n";

但如果我以这种方式更改调用者,它会打印出我认为的内容。所以很奇怪为什么$cat对象在$dog被实例化之后被覆盖了?

package Main;

my $cat = new Cat('Mily');
print $cat->get_name() . ": " . $cat->talk() , "\n"; 

my $dog = new Dog('Sat');
print $dog->get_name() . ": " . $dog->talk() , "\n";

【问题讨论】:

【参考方案1】:

为什么要祝福成一个全局变量?将您的构造函数更改为:

sub new 
    my $obj = shift;
    my $name = shift;
    my %pkg = ( 'name' => $name );
    bless \%pkg, $obj;
    return \%pkg;

更好的是,把它改成更惯用的东西:

sub new 
    my $class = shift;
    my $name  = shift;
    my $self  =  name => $name ;
    return bless $self, $class;

继续:

为什么要在每种动物中都使用newget_name?这两种方法都可以继承。当我们这样做的时候,我们不妨摆脱 @ISA 的麻烦:

package Animal;
sub new 
    my $class = shift;
    my $name  = shift;
    my $self  =  name => $name ;
    return bless $self, $class;


sub get_name 
    my $self = shift;
    return $self->'name';


package Cat;
use base qw/ Animal /;

sub talk 
    my $self = shift;
    return "Meow! Meow!";


package Dog;
use base qw/ Animal /;

sub talk 
    my $self = shift;
    return "Woof! Woof!";


package Main;

my $cat = Cat->new('Mike');
my $dog = Dog->new('Sat');

print $cat->get_name() . ": " . $cat->talk() , "\n"; 
print $dog->get_name() . ": " . $dog->talk() , "\n";

请问您正在学习哪本教程或书籍?

虽然上述方法非常好,但您不妨使用 Modern Perl 的方式:

package Animal;
use Moose;
has name => ( required => 1, is => 'rw', isa => 'Str' );

package Cat;
use Moose;
extends 'Animal';

has talk => ( default => "Meow! Meow!", is => 'ro' );

package Dog;
use Moose;
extends 'Animal';

has talk => ( default => "Woof! Woof!", is => 'ro' );

package Main;
my $cat = Cat->new( name => 'Mike');
my $dog = Dog->new( name => 'Sat');

print $cat->name . ": " . $cat->talk , "\n"; 
print $dog->name . ": " . $dog->talk , "\n";

【讨论】:

嗯,是的。好的 OO 设计总是很难;语言所能做的最好的事情就是不妨碍你。但我认为 Moose 让你更容易感受到设计的正确性。以上面的例子为例。 talk 使用了一个方法,但它实际上只是一个属性。因此,它应该是基类中的一个方法,它使用子类中的另一个属性。我发现很难以老式的形式看到这样的东西。 @innaM 非常感谢您的帮助。让我解释一下。 1. 我的 Dog 实例覆盖了我的 Cat 我真的很困惑,所以我想检查每个类的详细信息。我并不是真的要在每个类中定义 get_name 。 2. 我曾经使用过很多非常旧的 Unix 机器,所以我必须在 Perl 5.8 中编写代码,并且必须避免编写任何比它更新的东西。 :-( . 3. 专门针对这个问题,它是我在网络某处遇到的一个 Python 程序,我想翻译成 Perl 程序。但不幸的是滑倒了它。【参考方案2】:

简而言之:our 声明了包变量,因此每次执行 our %pkg = (...) 时,都会为同一个变量分配一个新值。由于所有\%pkg 引用都指向同一个var,所以new 的所有返回值都是同一个对象。一个引用只能被祝福到一个类中,所以最后一个获胜。

只需将our 更改为my,它应该可以按预期工作。

【讨论】:

【参考方案3】:

您已经声明了用于存储实例数据的变量

our %pkg

这是单个数据结构 (%Animal::pkg) 的别名,因此您的所有对象都使用相同的哈希。将our 更改为my,以便每次都创建一个新的哈希。


可能值得注意的是,Perl 中的“由内而外”对象可以并且确实使用包中的共享数据结构来存储实例数据,但需要额外的抽象级别才能使其工作,我不会'不建议从它们开始 OO Perl,它们是后天习得的。

【讨论】:

以上是关于为啥perl对象实例会互相覆盖的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的类节点会覆盖自己而不是创建新的节点对象

为啥打字稿允许我覆盖一个空的状态对象?

为啥覆盖会更改列名

Pytest 和覆盖率:为啥覆盖率结果会随目录结构而变化?

为啥 UIButton 会覆盖 UITapGestureRecognizer?

为啥在 Django 管理员的 save() 覆盖中将站点添加到对象似乎不起作用?