3 марта 2009 в 20:56

Атрибуты: введение

Perl*
Атрибуты появились в Perl довольно давно. Знаменательная для старожилов версия Perl 5.005 содержала довольно бесполезную прагму attrs. Прагма эта позволяла навесить на функцию пару встроенных атрибутов, для чего требовалось указать внутри этой функции use attrs с соответствующими параметрами. Правда, уже в марте 2000 года в Perl 5.6 эта прагма была объявлена deprecated. На замену ей пришел модуль attributes, а также расширенный синтаксис для объявления переменных/функций. Стало возможным создавать для них собственные метки и вводить для этих меток обработчики. По непонятным причинам, популярность атрибуты так и не получили. А жаль.

Ближе к сути


Сейчас их наверняка назвали бы не атрибутами, а тегами. Выглядит все просто — к объявляемой переменной или функции можно дописать несколько меток. Есть несколько стандартных для Perl атрибутов, но можно вводить и свои собственные. Встроенные атрибуты записываются строчными буквами, и потому использовать такой регистр для пользовательских меток не рекомендуется — во избежание накладок в будущем и варнингов в настоящем.

Синтаксис атрибутов достаточно понятен:

my $myVar : myAttribute = 10;
sub mySub : myAttribute1 : myAttribute2 {:}


Интерпретатор не накладывает ограничений на наличие пробела между двоеточием и названием атрибута, лично мне кажется более наглядным, когда они записаны слитно.

Атрибуты можно навешивать на функции и на переменные — скаляры, хеши, массивы.

Для чего это нужно


За примерами ходить недалеко — на CPAN живет группа модулей, объединенных под именем Attribute::Util. Я не буду комментировать возможность их применения в реальных проектах, но как простая и наглядная иллюстрация — это то, что нужно.

Первый из них, Attribute::Abstract, предлагает замену для классического описания абстрактных методов. Обратите на него внимание, если у вас есть подобные объявления:

sub myMethod { die "Abstract method called!"; }

Предлагаемый им синтаксис гораздо нагляднее:

sub myMethod: Abstract;

Второй класс, Attribute::Memoize, позволяет добавить кеширование к функциям, которые возвращают постоянный результат для одинаковых наборов аргументов. Конечно, завернуть код функции в if-блок труда не составит, но опять же — можно просто пометить функцию атрибутом :Memoize и знать, что ее результат кешируется.

Третий класс, Attribute::Method, позволяет избавиться от написания первой строки практически любого метода:

my $self = shift;

Просто ставим к методу атрибут :Method, и $self появляется сам собой. Кроме того, можно указать, какие аргументы переданы методу — тогда они тоже будут проинициализированы:

sub myMethod :Method($x) { print "self=$self, x=$x\n" }

Интересно? Тогда не забудьте заглянуть в исходный код модулей.

UPD: продолжение статьи: Атрибуты: взгляд внутрь
Bambr @Bambr
карма
65,2
рейтинг 0,0
Похожие публикации
Самое читаемое Разработка

Комментарии (31)

  • +2
    Статья готовилась для блога «Великий язык PERL!», но кармы пока маловато. Перенесу при первой возможности.
  • +1
    замечательно :) требую настойчиво продолжения!
  • 0
    в perl огромное количество фич, но юзается малая часть. так и с атрибутами.

    автор подобрал удачные примеры, но вот лень (моя) подсказывает что лучше придерживаться привычных схем в которых отсутствует сабж :)
    • 0
      А лень это чем-то обосновывает, или просто общается? :)
      • 0
        лень не обосновывает. она диктует свои условия. своеобразная оптимизация процессов жизнедеятельности:wq
  • 0
    Эдак можно реализовать декораторы, как я вижу. Мощно.
    • 0
      Ну в catalyst так и делают вобщем-то, sub index :uri('/lala'){}

      Атрибутами можно много фана сделать, но у них есть свои подводные камни :)
      • 0
        Поделитесь, какие камни достались вам. Я пока нашел только один, он описан во второй статье. Но не хотелось бы собирать их самостоятельно и дальше. К сожалению, по теме не так много информации как хотелось бы…
  • 0
    На сколько я понимаю это широко используется в перловом Catalyst-е
  • –1
    1
    • 0
      извините, промахнулась окном(((
      • 0
        Да в Catalyst это используется повсеместно. Могу сказать, что используется оно обосновано. Код становиться гораздо нагляднее. Мне вообще иногда начинало казаться, что я книгу читаю )
  • +1
    Я время от времени пытаюсь использовать редкие фичи, например фильтры, но натыкаюсь на случаи, когда они не работают или работают нетривиально. А работают программы у меня от Perl 5.6 до последних версий.
    Реально атрбуты, как мне кажется, только способствуют запутыванию кода.
    sub myMethod :Method($x) выглядит красиво, но не читается теми, кто атрибуты не использует Написать
    sub abc {my $x = shift;}
    несложно, зато нет никаких лишних зависимостей и всё понятно.
    • 0
      по поводу читабельности согласен. трудно предугадать кто будет поддерживать код. некоторые все еще пишут return undef; в своих творениях :)
      • 0
        И правильно делают, кстати.
        • 0
          а как же постулаты Perl Best Practices? O o
          • 0
            А кто их читает. Perl — язык для практиков. Главное — чтобы было удобно. И так умолчаний слишком много.
            • 0
              > А кто их читает
              Вот и получается write-only
              • +1
                Два Perl-кодера иногда могут не понимать код друг друга… иногда бывает обидно, а что еще хуже препятсвует дальнейшей разработке.
                В других языках проще с этим, так как они более менее стандартизированы. А на Perl тебе ничего не мешает свой язык написать, в котором уж никто ничего без тебя не поймет — обфускация )
                Впрочем даже в C практиковались не очень хорошие практики, видел код
                #define if if (
                #define then ) {
                #define else } else {
                #define endif }

                Комуто ведь было удобнее писать
                if a then
                b = ...;
                else
                b = ...;
                endif
          • 0
            Для программиста одиночки — это лишние заморочки!

            Их применение важно при работе в команде, это своего рода стандарт кодирования без которого командная работа просто превратится в хаос и все друг друга поубивают в спорах у кого лучше.
          • 0
            PBP рекомендует явные return-ы (section 9.11).

            Что касается return undef, то это вполне правильная команда, если функция должна вернуть скаляр. Отличающаяся от просто return. Использование одной, когда должна быть использована другая — баг. Так что return-ы всякие нужны, return-ы всякие важны! :)
    • 0
      Ну для такой ерунды их использовать конечно не стоит. Я привел этот пример, т.к. он меня действительно удивил. Когда я посмотрел на реализацию этого модуля, у меня случился небольшой нервный срыв :)

      А вот пример с memoize выглядит уже более здраво. Есть типичный декоратор, реализующий одинаковое для любой функции поведение, пусть даже тривиальное. Копипастить его ручками — только зря код плодить, а так получается достаточно наглядно. У меня через подобный паттерн работает гораздо более хитрая система, и пока что я счастлив, сравнивая ее с тем что было раньше — новое поведение прикручивается не за час с хреном, а за 5 минут.

      Хотя я согласен, что такие трюки заслуживают документирования в первую очередь — хотя бы ввиду того, что не каждый знает про подобный синтаксис.
      • 0
        Это всё заплаты для сокрытия того факта, что ООП модель перла очень простая, гибкая, но слабоконтролируемая. Мне кажется, что использование чего-то, выходящего за базовые возможности, сильно бьёт по надёжности программы. Даже кэширование memoize вызывает вопросы — а какие алгоритмы для удаления из кэша, где хранится результат…
        • 0
          Если Вы реализуете то же самое через вполне базовое обращение к внешней функции, вопросы останутся и придется точно так же эти алгоритмы искать в другом месте. А по поводу надежности — аргументируйте, пока утверждение ничем не подкреплено. Я планирую через некоторое время написать о том, как атрибуты помогли навести порядок в одном достаточно крупном проекте. Новый код работает гораздо стабильнее и прозрачнее старого. Это конечно больше говорит о проблемах в старом коде, я могу также сделать вывод что надежность из-за новой фичи не упала.
          • 0
            Аргумент простой — все нововведения ненадёжны в принципе. В любой области. Если есть хорошая тестовая база — то ошибки быстро находятся, если нет — медленно. В данном случае тестовая база крохотная.

            Реализовать свой алгоритм кэширования для более сложной задачи, чем кэширования sin(x), всё равно придётся.

            Если проект сложный, то, наверно, можно вложиться в такого рода технологии, но у меня есть подозрение, что сложный проект на перле в принципе невозможно реализовать — перл морально устарел, поддерживать проекты на нём трудно.

            • 0
              Это уже давно не нововведение…
              • 0
                впрочем ладно. Я понял Вашу мысль :)
    • 0
      На счет читабельности вопрос, как всегда, спорный. Как раз для прототипов синтаксис обычный (например, Javascript). Как и для тех кто работал с блоками кода и н
      • 0
        А прототипы тут причём?

        А что такое блоки кода?
  • 0
    Большое спасибо за статью! Давненько ничего не было по перлу, а жаль. С интересом прочитаю продолжение.
  • 0
    Кусок из Attribute::Method:

    my $dp        = B::Deparse->new();
    ...
    sub UNIVERSAL::Method : ATTR(RAWDATA) {
        my ( $pkg, $sym, $ref, undef, $args ) = @_;
        my $src = $dp->coderef2text($ref);
        if ($args) {
            $src =~ s/\{/sub{\nmy \$self = shift; my ($args) = \@_;\n/;
        }
        else {
            $src =~ s/\{/sub{\nmy \$self = shift;\n/;
        }
        no warnings 'redefine';
        *$sym = eval qq{ package $pkg; $src };
    }
    

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.