Объектно-ориентированный синтаксис конструктора Perl и именованные параметры
Я немного смущен тем, что происходит в конструкторах 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;
}
И еще один пример:
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;
}
И вот script, который использует эти объекты:
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";
Я ввел вопросы/путаницу, которые у меня были в коде как комментарии.
Ответы
Ответ 1
Чтобы ответить на главный вопрос вашего вопроса, так как хэш может быть инициализирован как список пар key => value
, вы можете отправить такой список в функцию, а затем присвойте @_
хешу. Это стандартный способ выполнения именованных параметров в Perl.
Например,
sub foo {
my %stuff = @_;
...
}
foo( beer => 'good', vodka => 'great' );
Это приведет к %stuff
в подпрограмме foo
, имеющей хэш с двумя ключами, beer
и vodka
, и соответствующие значения.
Теперь, в OO Perl, есть некоторые дополнительные морщины. Всякий раз, когда вы используете оператор стрелки (->
) для вызова метода, все, что было в левой части стрелки, застревает в начале массива @_
.
Итак, если вы скажете Foo->new( 1, 2, 3 )
;
Затем внутри вашего конструктора @_
будет выглядеть так: ( 'Foo', 1, 2, 3 )
.
Таким образом, мы используем shift
, который без аргумента действует на @_
неявно, чтобы получить этот первый элемент из @_
и назначить его $type
. После этого @_
имеет только наши пары имя/значение, и мы можем назначить его непосредственно хешу для удобства.
Затем мы используем это значение $type
для bless
. Все bless
does принимает ссылку (в вашем первом примере хеш-код) и говорят, что "эта ссылка связана с определенным пакетом". Alakazzam, у вас есть объект.
Помните, что $type
содержит строку "Foo", которая является именем нашего пакета. Если вы не укажете второй аргумент bless
, он будет использовать имя текущего пакета, который также будет работать в этом примере, но не будет работать для унаследованных конструкторов.
Ответ 2
0,1. В Perl конструктор - это просто подпрограмма, называемая new.
Да, по соглашению new
- это конструктор. Он также может выполнять инициализацию или нет. new
должен возвращать объект с успехом или вызывать исключение (die
/croak
), если произошла ошибка, которая предотвращает создание объекта.
Вы можете назвать свой конструктор чем угодно, иметь столько конструкторов, сколько хотите, и даже создавать благословные объекты в любое пространство имен, которое вы хотите (не это хорошая идея).
0,2. Я не понимаю, что делает my $type = shift;
, но я всегда это вижу. Мне это нужно?
shift
без аргументов выводит аргумент с заголовка @_
и назначает его $type
. Оператор ->
передает invocant (левая сторона) в качестве первого аргумента подпрограммы. Таким образом, эта строка получает имя класса из списка аргументов. И да, вам это нужно.
0,3. Как массив входных данных становится хешем %params
? my %params = @_;
Назначение в хэш выполняется в контексте списка, причем пары элементов списка группируются как пары ключ/значение. Итак, %foo = 1, 2, 3, 4;
, создает хэш, такой, что $foo{1} == 2
и $foo{3} == 4
. Обычно это делается для создания именованных параметров для подпрограммы. Если sub передано нечетное число аргументов, предупреждение будет сгенерировано, если предупреждения включены.
0,4. Что означает "my $self = {};` do?
Эта строка создает анонимную хеш-ссылку и присваивает ее переменной с лексической областью $self
. Хеш-ссылка будет хранить данные для объекта. Как правило, ключи в хэше имеют сопоставление "один-к-одному" с атрибутами объекта. Поэтому, если класс Foo имеет атрибуты "размер" и "цвет", если вы проверяете содержимое объекта Foo, вы увидите что-то вроде $foo = { size => 'm', color => 'black' };
.
0,5. Учитывая $self->{'High'} = $params{'High'};
, где $params{'High'}
исходит из?
Этот код опирается на аргументы, переданные в new
. Если new
был вызван как 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;
}
Ответ 3
Ваш вопрос не о OO Perl. Вы смущены структурами данных.
Хэш может быть инициализирован с использованием списка или массива:
my @x = ('High' => 42, 'Low' => 11);
my %h = @x;
use Data::Dumper;
print Dumper \%h;
$VAR1 = {
'Low' => 11,
'High' => 42
};
Когда вы вызываете метод в ссылке bless
ed, ссылка добавляется к списку аргументов, который получает метод:
#!/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);
Вывод:
$VAR1 = [bless( [], 'My::Mod' ),'High',42,'Low',11];
$VAR1 = ['My::Mod','High',42,'Low',11];
$VAR1 = ['High',42,'Low',11];
Ответ 4
Некоторые моменты, которые еще не были рассмотрены:
В Perl конструктор - это просто подпрограмма new
.
Не совсем. Вызов нового конструктора - это просто соглашение. Вы можете называть это чем угодно. Нет ничего особенного в этом имени с точки зрения perl.
bless $self, $type;
Оба из ваших примеров не возвращают результат благословения явно. Я надеюсь, что вы знаете, что они так неявно в любом случае.
Ответ 5
Если вы назначаете массив хешу, perl обрабатывает переменные элементы в массиве как ключи и значения. Ваш массив выглядит как
my @array = (key1, val1, key2, val2, key3, val3, ...);
Когда вы назначаете это% hash, вы получаете
my %hash = @array;
# %hash = ( key1 => val1, key2 => val2, key3 => val3, ...);
Это еще один способ сказать, что в синтаксисе строкового списка/хэш-конструкции ","
и "=>"
означают одно и то же.
Ответ 6
В Perl все аргументы для подпрограмм передаются через предопределенный массив @_
.
shift
удаляет и возвращает первый элемент из массива @_
. В Perl OO это метод invocant - обычно это имя класса для конструкторов и объект для других методов.
Хеши выравниваются и могут быть инициализированы списками. Это общий трюк для эмуляции названных аргументов для подпрограмм. например.
Employee->new(name => 'Fred Flintstone', occupation => 'quarry worker');
Игнорируя имя класса (которое смещено), нечетные элементы становятся хеш-ключами, а четные элементы становятся соответствующими значениями.
my $self = {}
создает новую хеш-ссылку для хранения данных экземпляра. Функция bless
- это то, что превращает нормальную хеш-ссылку $self
в объект. Все, что он делает, это добавить некоторые метаданные, которые идентифицируют ссылку как принадлежащую к классу.
Ответ 7
Да, я знаю, что здесь я немного некромант, но...
Хотя все эти ответы превосходны, я подумал, что я бы упомянул Moose. Moose упрощает конструкторы (package Foo;use Moose;
автоматически предоставляет конструктор с именем new
(хотя имя "новое" может быть переопределено, если вы хотите)), но doesn 't уберите любую конфигурацию, если вам это нужно.
Как только я просмотрел документацию для Moose (что довольно хорошо в целом, и есть много дополнительных фрагментов учебника, если вы правильно относитесь к Google), я никогда не оглядывался назад.