Как работают атрибуты метода Perl?

47

Известная встроенная функция Perl - это атрибуты. Однако официальная документация делает довольно плохую работу по внедрению новичков в концепцию. В то же время, такие каркасы, как Catalyst, широко используют атрибуты, которые, похоже, делают многое там проще. Так как использование чего-то, не зная, что последствия всасываются немного, я хотел бы узнать подробности. Синтаксически они выглядят как декораторы Python, но документация подразумевает что-то более простое.

Не могли бы вы объяснить (если возможно, примеры из реального мира), какие атрибуты подходят и что происходит за дверью?

Теги:
attributes

3 ответа

35
Лучший ответ

Вы правы, документация не очень понятна в этой области, тем более что атрибуты не так сложны. Если вы определяете атрибут подпрограммы, например:

sub some_method :Foo { }

Perl будет при компиляции вашей программы (это важно) ищите магический sub MODIFY_CODE_ATTRIBUTES в текущем пакете или любом из его родительских классов. Это будет вызываться с именем текущего пакета, ссылкой на вашу подпрограмму и списком атрибутов, определенных для этой подпрограммы. Если этот обработчик не существует, компиляция завершится неудачно.

То, что вы делаете в этом обработчике, полностью зависит от вас. Да, это правильно. Никакой скрытой магии вообще. Если вы хотите сообщить об ошибке, то при возврате имени нарушающих атрибутов компиляция завершится с сообщением "недействительный атрибут".

Существует еще один обработчик, называемый FETCH_CODE_ATTRIBUTES, который будет вызываться всякий раз, когда кто-то говорит

use attributes;
my @attrs = attributes::get(\&some_method);

Этот обработчик получает переданное имя пакета и подпрограмму и должен возвращать список атрибутов подпрограммы (хотя то, что вы действительно делаете, снова зависит от вас).

Вот пример, чтобы включить простую "маркировку" методов с произвольными атрибутами, которые вы можете запросить позже:

package MyClass;
use Scalar::Util qw( refaddr );

my %attrs; # package variable to store attribute lists by coderef address

sub MODIFY_CODE_ATTRIBUTES {
    my ($package, $subref, @attrs) = @_;
    $attrs{ refaddr $subref } = \@attrs;
    return;
}

sub FETCH_CODE_ATTRIBUTES {
    my ($package, $subref) = @_;
    my $attrs = $attrs{ refaddr $subref };
    return @$attrs;
}

1;

Теперь в MyClass и во всех его подклассах вы можете использовать произвольные атрибуты и запрашивать их с помощью attributes::get():

package SomeClass;
use base 'MyClass';
use attributes;

# set attributes
sub hello :Foo :Bar { }

# query attributes
print "hello() in SomeClass has attributes: ",
      join ', ', attributes::get(SomeClass->can('hello'));

1;
__END__
hello() in SomeClass has attributes: Foo, Bar

В целом, атрибуты не делают очень многого, что, с другой стороны, делает их очень гибкими: вы можете использовать их в качестве реальных "атрибутов" (как показано в этом примере), реализовать что-то вроде декораторов (см. ответ Синан) или для ваших собственных коварных целей.

  • 0
    Отличный ответ !!! : D
11
  • 1
    @ Брайан Спасибо за исправление. Я собираюсь обвинить в избирательном восприятии то, что в списке авторов на этой странице выбрано только ваше имя ;-) Теперь, когда я снова посмотрю на него, очевидно, что Майк Фридман является автором.
5

Атрибуты - одна из вещей, которые, если вы не знаете, как их использовать, вы не должны беспокоиться о них. Однажды я сделал атрибут database_method, чтобы указать системе, что набор записей будет запрошен до ввода этого метода, и что метод знал, что основные входы будут поступать из хранимой процедуры, к которой он соответствовал.

Я использовал атрибуты для обертывания фактических, указанных действий с этими данными. Итак, одна из действительно кажущихся полезными идей заключается в том, чтобы обернуть методы с косвенностью, но было труднее сделать работу с вызывающим, не переопределяя ее. В конце он был слишком заметен как функция "только для экспертов" и потребовал бы поддержки для отслеживания тайных интранов - чего-то, чего вы хотите избежать, если вы пишете Perl в магазине perl-shop.


Люди могут захотеть проголосовать за меня, но я беру из статьи, цитируемой Синаном:

Предостережение

Хотя это мощный метод, он не идеален. Код не будет надлежащим образом обертывать анонимные подпрограммы, и он не обязательно будет распространять контекст вызова на обернутые функции. Кроме того, использование этой методики значительно увеличит количество диспетчеров подпрограмм, которые ваша программа должна выполнить во время выполнения. В зависимости от сложности вашей программы это может значительно увеличить размер стека вызовов. Если скорость ослепления является основной целью проекта, эта стратегия может быть не для вас.

Это существенные недостатки, если вы не хотите переопределить caller. Меня не волнует "ослепляющая скорость", и я почти готов попробовать свои силы при переопределении caller, чтобы обойти любую подпрограмму, которая регистрируется как "DO_NOT_REPORT", - но у меня есть некоторая безобидная кодировка, которая еще не избили меня.

Даже статья допускает, насколько плохо документирована эта функция, и содержит это предостережение. Скажите мне, когда еще было неплохо использовать увлекательную, неясную особенность? Это достаточно часто, люди вставляют в пространство имен UNIVERSAL, чтобы избежать проблемы наследования.

(Но если вы считаете, что это плохой ответ, только один нижний нисходящий знак даст мне знак давления со стороны сверстников: D)

  • 2
    Верно, но эти предостережения применимы только к этому конкретному способу использования атрибутов, который заключается в переносе оригинального метода. В большинстве случаев, когда используются атрибуты (Catalyst и т. Д.), Они используются просто для тегирования (я думаю), что совсем не проблематично.
  • 0
    Тем не менее, это дано как одно из главных применений в одном из самых ясных обучающих программ Perl (тот, который спас бы меня некоторое время.) На предмете. Я должен признать, что я еще не зарегистрировался в Catalyst.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню