Атрибуты Moo/Moose - как работает "keys %$self"?
В своем последнем вопросе я задал много несвязанных вещей и не могу принять несколько ответов, которые отвечают только на некоторые вопросы, поэтому здесь четко (я надеюсь) определен вопрос об (Moo) атрибутах.
use 5.010;
use strict;
use warnings;
package SomeMoo;
use Moo;
has $_ => ( is => 'rw', predicate => 1) for (qw(a1 a2 nn xx));
package SomeMoose;
use Moose;
has $_ => ( is => 'rw', predicate => "has_".$_) for (qw(a1 a2 nn xx));
package main;
use Data::Dumper;
my $omoo = SomeMoo->new(a1 => "a1val", a2 => "a2val", xx=>"xxval");
say Dumper $omoo;
# $VAR1 = bless( {
# 'a2' => 'a2val',
# 'a1' => 'a1val',
# 'xx' => 'xxval'
# }, 'SomeMoo' );
#for Moose:
my $omoose = SomeMoose->new(a1 => "a1val", a2 => "a2val", xx=>"xxval");
say Dumper $omoose;
#as above, only blessed to package 'SomeMoose'.
#in both cases can do the next, for getting an (partial) list "attributes"
say $_ for keys (%$omoose); #or %$omoo
#anyway, in Moose i can
say "all attributes";
say $_->name for $omoose->meta->get_all_attributes();
#what prints
# all attributes
# nn
# a2
# a1
# xx
Таким образом, благословенный объект защищает объект, который содержит только установленные атрибуты.
Вопрос:
- Почему
$self
ссылки, (так что%$self
conatains) только те атрибуты, которые установлены, и не все, напримерnn
тоже из примера кода? (Когдаbless
только связываетreference
сpackage
Почему$omoo
не содержит все переменные пакета?) И откуда Moose знает это?) - как получить all_attributes в случае Moo?
Очевидно, мне не хватает некоторых базовых знаний..:(
1 ответ
почему $self ссылается (так что%$self содержит) только те атрибуты, которые установлены, а не все, например nn тоже из примера кода? (Когда благословение связывает только ссылку с пакетом, почему $omoo не содержит всех переменных пакета?) И откуда Moose знает это?)
Каждый раз, когда вы создаете новый объект, такой как SomeMoo->new
вы создаете новую ссылку, которая состоит из двух частей, первая - это данные, которые обычно являются хэш-ссылками, которые хэш-ссылки благословляются второй частью, которая является классом, SomeMoo
, Хеш используется для хранения данных, связанных с этим экземпляром нового объекта, в то время как SomeMoo
это класс, который предоставляет методы, связанные с доступом / манипулированием / работой с данными.
Внутри ваших определений классов вы вызываете функцию с именем has
, Эта функция расширяет класс новыми методами. В случае с Moo вы могли бы сделать версию для бедняка, написав что-то вроде:
package SimpleMoo;
no strict 'refs';
sub new {bless {}, $_[0]} # very very simple constructor (oldskool)
*{__PACKAGE__."::$_"} = sub { # fyi: __PACKAGE__ eq "SimpleMoo"
$_[0]->{$_} = $_[1] if exists $_[1];
return $_[0]->{$_};
} for qw(a1 a2 nn xx);
Выше добавлены все наши методы к классу SimpleMoo, это происходит посредством манипулирования таблицей символов и, вероятно, довольно близко к тому, что делает Moo (за исключением того, что Moo/Moose расширяет мета-класс, от которого наследуется ваш класс), еще более простой пример Чтобы определить методы доступа вручную:
package SimpleMoo;
sub new {bless {}, $_[0]} # very very simple constructor (oldskool)
sub a1 {
my ($self) = shift;
$self->{a1} = $_[0] if exists $_[0]; # set value if provided
return $self->{a1}; # return the value
}
sub a2 {
my ($self) = shift;
$self->{a2} = $_[0] if exists $_[0]; # set value if provided
return $self->{a2}; # return the value
}
# ... copy and paste the for nn and xx ...
Как вы можете видеть из приведенных выше примеров, я расширяю класс методами, которые позволяют вам устанавливать и получать значения из хеша, но не подразумевают неявную настройку структуры хеша как части конструктора объекта (который должен происходить внутри new
если хочешь сделать это) - точно так же как Му и Мус.
Это означает, что осмотр объекта через что-то вроде keys %$omoo
не показывает какие-либо ключи, пока они не были установлены (либо через вызов ->new
или установив их с $omoo->a1("someValue")
,
Как уже упоминалось Ikegami, добавив что-то вроде: default => undef
на ваш has
операторы заставят Moo/Moose создать экземпляр значения, когда объект создается впервые.
Я надеюсь, что это объясняет, как $self
содержит данные объекта, которые содержат только ключи, предварительно установленные методами или конструкторами.
как получить all_attributes в случае Moo?
К сожалению, Moo api не предоставляет метода для перечисления всех определенных атрибутов, поэтому я не могу предложить поддерживаемое решение, но могу я предложить одно из многих (неидеальных, крайне не рекомендуемых) решений:
package SimpleMoo;
use Moo;
our @attr_list;
sub my_has {push @attr_list, $_[0]; has @_}
sub get_all_attributes {@attr_list}
my_has $_ => ( is => 'rw', predicate => 1) for (qw(a1 a2 nn xx));
package main;
my $omoo = SimpleMoo->new();
say $_ for $omoo->get_all_attributes;
Я уверен, что кто-то из Moo сможет предоставить лучший способ доступа к мета-классу, используемому в Moo для получения этого списка. Но я уверен, что любое решение не рекомендуется.
В конечном счете, вы используете Moose и Moo, вам не нужно проверять базовый объект, оба модуля используют магию (модификацию таблицы символов и наследование классов и все виды), и вы не можете полагаться на базовый объект или сам класс, чтобы иметь атрибуты и методы вы ожидаете их иметь. если у вас есть сценарий использования, в котором вы хотите перечислить каждый атрибут, определенный в классе, используйте Moose и осмотрите мета класс, или возьмите под свой контроль свой класс, определив его вручную.
Вам также следует подумать, зачем вам нужен список атрибутов объекта, возможно, вы могли бы перечислить их самостоятельно? Что делать, если кто-то наследует от вашего класса? или вы решили смешать роль? нарушит ли это любой код, пытающийся использовать список атрибутов вашего объекта?