面向对象的Perl构造函数语法和命名参数

Tho*_*ens 27 oop perl constructor instance-variables

我对Perl构造函数中发生的事情感到有点困惑.我发现这两个例子是perldoc perlbot.

package Foo;

#In Perl, the constructor is just a subroutine called new.
sub new {
  #I don't get what this line does at all, but I always see it. Do I need it?
  my $type = shift;

  #I'm turning the array of inputs into a hash, called parameters.
  my %params = @_;

  #I'm making a new hash called $self to store my instance variables?
  my $self = {};

  #I'm adding two values to the instance variables called "High" and "Low".
  #But I'm not sure how $params{'High'} has any meaning, since it was an
  #array, and I turned it into a hash.
  $self->{'High'} = $params{'High'};
  $self->{'Low'} = $params{'Low'};

  #Even though I read the page on [bless][2], I still don't get what it does.
  bless $self, $type;
}
Run Code Online (Sandbox Code Playgroud)

另一个例子是:

package Bar;

sub new {
  my $type = shift;

  #I still don't see how I can just turn an array into a hash and expect things
  #to work out for me.
  my %params = @_;
  my $self = [];

  #Exactly where did params{'Left'} and params{'Right'} come from?
  $self->[0] = $params{'Left'};
  $self->[1] = $params{'Right'};

  #and again with the bless.
  bless $self, $type;
}
Run Code Online (Sandbox Code Playgroud)

这是使用这些对象的脚本:

package main;

$a = Foo->new( 'High' => 42, 'Low' => 11 );
print "High=$a->{'High'}\n";
print "Low=$a->{'Low'}\n";

$b = Bar->new( 'Left' => 78, 'Right' => 40 );
print "Left=$b->[0]\n";
print "Right=$b->[1]\n";
Run Code Online (Sandbox Code Playgroud)

我已将问题/混淆注入代码作为注释.

fri*_*edo 53

要回答问题的主旨,因为哈希可以初始化为对列表key => value,您可以将这样的列表发送到函数,然后分配@_给哈希.这是在Perl中执行命名参数的标准方法.

例如,

sub foo { 
    my %stuff = @_;
    ...
}

foo( beer => 'good', vodka => 'great' );
Run Code Online (Sandbox Code Playgroud)

这将导致%stuff在子程序foo具有两个密钥,散列beervodka,和相应的值.

现在,在OO Perl中,还有一些额外的皱纹.每当使用arrow(->)运算符调用方法时,箭头左侧的任何内容都会粘贴到@_数组的开头.

所以,如果你说Foo->new( 1, 2, 3 );

然后在你的构造函数中,@_看起来像这样:( 'Foo', 1, 2, 3 ).

所以我们使用shift没有参数@_隐式操作来获取第一个项目@_,并将其分配给$type.之后,@_只剩下我们的名称/值对,我们可以将它直接分配给哈希以方便使用.

然后我们使用该$typebless.所有的bless工作都是引用(在第一个示例中为哈希引用),并说"此引用与特定包相关联".Alakazzam,你有一个对象.

请记住,它$type包含字符串'Foo',这是我们的包的名称.如果指定第二个参数bless,则它将使用当前包的名称,该名称也将在此示例中起作用,但不适用于继承的构造函数.

  • 你可以做'我的($ self,%stuff)= @_;`而不是转移. (8认同)

dao*_*oad 18

0.1.在Perl中,构造函数只是一个名为new的子例程.

是的,按照惯例new是一个构造函数.它也可以执行初始化. new应该在成功时返回一个对象,或者如果发生了阻止对象创建的错误则抛出异常(die/ croak).

您可以根据自己的喜好为构造函数命名,拥有尽可能多的构造函数,甚至可以将bless对象构建到您想要的任何名称空间中(这不是一个好主意).

0.2.我根本得不到什么my $type = shift;,但我总是看到它.我需要它吗?

shift没有任何参数可以从头开始@_并将其分配给$type.的->操作者经过调用者(左手侧)作为第一个参数的子程序.所以这一行从参数列表中获取类名.而且,是的,你确实需要它.

0.3.一组输入如何成为%params 哈希?my %params = @_;

在列表上下文中完成对散列的分配,其中列表项对被分组为键/值对.因此%foo = 1, 2, 3, 4;,创建一个哈希,使得$foo{1} == 2$foo{3} == 4.通常这样做是为了为子例程创建命名参数.如果sub传递了奇数个参数,则在启用警告时将生成警告.

0.4.什么'我的$ self = {};`做什么?

此行创建一个匿名哈希引用,并将其分配给词法范围的变量$self.哈希引用将存储对象的数据.通常,散列中的键具有与对象属性的一对一映射.因此,如果类Foo具有属性'size'和'color',那么如果检查Foo对象的内容,您将看到类似的内容$foo = { size => 'm', color => 'black' };.

0.5.鉴于$self->{'High'} = $params{'High'};哪里$params{'High'}来了?

此代码依赖于传递给的参数new.如果new被称为like Foo->new( High => 46 ),那么根据问题3创建的哈希将具有键的值High(46).在这种情况下,它相当于说$self->{High} = 46.但是,如果这个方法被调用,Foo->new()那么没有价值可用,而且我们有$self->{High} = undef.

0.6.怎么bless办?

bless获取引用并与特定包关联,以便您可以使用它来进行方法调用.使用一个参数,引用与当前包相关联.使用两个参数,第二个参数指定要将引用与之关联的包.最好始终使用两个参数形式,以便您的构造函数可以由子类继承并仍然可以正常运行.

最后,我将重写基于哈希的对象访问器,因为我将使用经典的OO Perl编写它.

package Foo;

use strict;
use warnings;
use Carp qw(croak);

sub new {
    my $class = shift;

    croak "Illegal parameter list has odd number of values" 
        if @_ % 2;

    my %params = @_;

    my $self = {};
    bless $self, $class;

    # This could be abstracted out into a method call if you 
    # expect to need to override this check.
    for my $required (qw{ name rank serial_number  });
        croak "Required parameter '$required' not passed to '$class' constructor"
            unless exists $params{$required};  
    }

    # initialize all attributes by passing arguments to accessor methods.
    for my $attrib ( keys %params ) {

        croak "Invalid parameter '$attrib' passed to '$class' constructor"
            unless $self->can( $attrib );

        $self->$attrib( $params{$attrib} );
    }

    return $self;
}
Run Code Online (Sandbox Code Playgroud)

  • 你说新的应该返回undef,如果它不能创建一个对象.这不是一个硬性规定.有一个很好的论据,说构造函数应该返回一个对象或死亡,如果这是不可能的.这样,任何调用构造函数的人都需要处理可能的异常. (2认同)

Sin*_*nür 9

你的问题不是关于OO Perl.您对数据结构感到困惑.

可以使用列表或数组初始化哈希:

my @x = ('High' => 42, 'Low' => 11);
my %h = @x;

use Data::Dumper;
print Dumper \%h;
Run Code Online (Sandbox Code Playgroud)
$VAR1 = {
          'Low' => 11,
          'High' => 42
        };

blessed引用上调用方法时,引用将附加到方法接收的参数列表中:

#!/usr/bin/perl

package My::Mod;

use strict;
use warnings;

use Data::Dumper;
$Data::Dumper::Indent = 0;

sub new { bless [] => shift }

sub frobnicate { Dumper(\@_) }

package main;

use strict;
use warnings;

my $x = My::Mod->new;

# invoke instance method
print $x->frobnicate('High' => 42, 'Low' => 11);

# invoke class method
print My::Mod->frobnicate('High' => 42, 'Low' => 11);

# call sub frobnicate in package My::Mod
print My::Mod::frobnicate('High' => 42, 'Low' => 11);
Run Code Online (Sandbox Code Playgroud)

输出:

$VAR1 = [bless( [], 'My::Mod' ),'High',42,'Low',11];
$VAR1 = ['My::Mod','High',42,'Low',11];
$VAR1 = ['High',42,'Low',11];


inn*_*naM 9

尚未处理的一些要点:

在Perl中,构造函数只是一个名为的子例程new.

不完全的.调用构造函数new只是一个约定.你可以随便叫它.从perl的角度来看,这个名字并没有什么特别之处.

bless $self, $type;

你的两个例子都没有明确地回复祝福的结果.我希望你知道他们无论如何都是隐含的.


Cor*_*ter 8

如果将数组分配给散列,perl会将数组中的交替元素视为键和值.你的阵列看起来像

my @array = (key1, val1, key2, val2, key3, val3, ...);
Run Code Online (Sandbox Code Playgroud)

当你将它分配给%hash时,你会得到

my %hash = @array;
# %hash = ( key1 => val1, key2 => val2, key3 => val3, ...);
Run Code Online (Sandbox Code Playgroud)

在perl list/hash构造语法中,这是另一种说法,","并且"=>"意思相同.

  • +1但请注意,如果LHS仅由[A-Za-z0-9_]组成,则胖逗号(`=>`)会自动引用LHS. (2认同)

Mic*_*man 5

在Perl中,子例程的所有参数都通过预定义的数组传递@_.

shift去除并从返回的第一个项目@_排列.在Perl OO中,这是方法调用 - 通常是构造函数的类名和其他方法的对象.

哈希展平并可以按列表初始化.这是模拟子程序的命名参数的常用技巧.例如

Employee->new(name => 'Fred Flintstone', occupation => 'quarry worker');
Run Code Online (Sandbox Code Playgroud)

忽略类名(被移除)奇数元素变为散列键,偶数元素变为相应的值.

my $self = {}创建一个新的哈希参考举行的实例数据.该bless函数将普通哈希引用$self转换为对象.它所做的就是添加一些元数据,将元数据标识为属于该类.