2009-06-12 10 views
45

少し知られている組み込みのPerl機能は属性です。しかし、公式のdocumentationは、この概念に初心者を紹介するやや悪い仕事をしています。同時に、Catalystのようなフレームワークは、多くのことをより簡単にするような属性を広範囲に使用します。その意味を知らずに何かを使うと、ちょっと吸うから、詳細を知りたい。構文上、Pythonのデコレータのように見えますが、ドキュメンテーションはもっと簡単なことを暗示しています。Perlメソッドの属性はどのように機能しますか?

可能であれば実際の例を使って、どのような属性が良いのか、ドアの後ろには何が起こるのかを説明できますか?

答えて

36

あなたはそうです、特に、属性がそれほど複雑ではないので、この分野での文書はあまり明確ではありません。あなたは、サブルーチンの属性を定義する場合は、次のように:

sub some_method :Foo { } 

Perlがあなたのプログラムをコンパイル中には、(これは重要です)、現在のパッケージまたはその親クラスのいずれかで魔法のサブMODIFY_CODE_ATTRIBUTESを探します。これは、現在のパッケージの名前、サブルーチンへの参照、およびこのサブルーチン用に定義された属性のリストとともに呼び出されます。このハンドラが存在しない場合、コンパイルは失敗します。

このハンドラで行うことは、あなた次第です。はい、そうです。まったく隠された魔法はありません。エラーを通知する場合は、問題のある属性の名前を返すと、コンパイルに失敗し、「無効な属性」メッセージが表示されます。もし何かかわらず、(そこに誰かがこのハンドラは、パッケージ名とサブルーチンのリファレンスを渡さ

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

言うたびに呼び出されますFETCH_CODE_ATTRIBUTESと呼ばれる別のハンドラがあり、かつサブルーチンの属性のリストを返すことになっている

本当にやるのはあなた次第です)。ここで

は、後で照会することができ、任意の属性を持つメソッドの簡単な「タグ付け」を有効にする例です。

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 

要約すると、属性は逆に彼らは非常に柔軟になりそのあまり行わない:あなたは「属性」本物としてそれらを使用することができます(この例で示すように)、何かを実装しますのようなデコレータ(Sinan's answer)、またはあなたの邪悪な目的のために。

+0

優れた答え! :D – gideon

5

属性は、使用方法がわからない場合は気にする必要はありません。私は一度database_methodアトリビュートを作成しました。このメソッドに入る前にレコードセットが要求されることをシステムに示すため、メソッドはそれが対応するストアドプロシージャからのメイン入力であることを知っていました。

私は属性を使用して、指定された実際のアクションをそのデータでラップしていました。本当に一見有益なアイデアの1つは、間接指定でメソッドをラップすることですが、オーバーライドすることなく呼び出し元の作業を行うことは難しくなりました。結局、それは "エキスパート専用"の機能としてはあまりにも目立つものでしたし、Perlを店に入れておけば、避けたいこともあります。


人々が私を否決することもできますが、私はシナンによって引用記事から取る:

警告

これは強力な技術ですが、それは完璧ではありません。 コードは匿名サブルーチンを適切にラップしません。呼び出しコンテキストをラップされた関数に必ずしも伝播するとは限りません。さらに、この手法を使用すると、プログラムが実行時に実行する必要があるサブルーチンディスパッチの数が大幅に増加します。プログラムの複雑さによっては、コールスタックのサイズが大幅に増加する可能性があります。目をくらます速度が大きな設計目標である場合、この戦略はあなたのためではないかもしれません。

あなたがcallerをオーバーライドするために喜んでいる場合を除き、これらは重要欠点があります。私は "まばたきのスピード"についてはあまり気にしません。私はcallerを無効にして自分自身を "DO_NOT_REPORT"として登録するサブルーチンをバイパスするように私の手を試したいと思っていますが、まだ私から殴られてしまった。

この記事でさえ、この機能がどのように不正確であるかを認めており、この警告が含まれています。奇妙な、あいまいな機能を使用するのは良いアイデアでしたか?多くの場合、人々は継承の問題を避けるために、UNIVERSAL名前空間を入れてしまいます。

(あなたはそれが悪いの答えだと思うんなら、ちょうど別のdownvoteが私に仲間からの圧力のバッジ与える:D)を

+2

真ですが、これらの注意事項は、元のメソッドをラップする属性を使用するこの特定の方法にのみ適用されます。アトリビュートが使用されるほとんどの場合(Catalystなど)、タグ付けには単に使用されます(私は思う)。これはまったく問題ありません。 – trendels

+0

これは、最も明白なPerlチュートリアルの1つ(主に私を時間の節約にしたもの)の主な用途の1つとして挙げられていますが、私はまだCatalystにチェックインしていないことを認めなければならない。 – Axeman

11

$perl->buzzMike Friedman's articleを推奨しています。

+1

@brian訂正ありがとうございます。私はそのページの著者のリストからあなたの名前だけを選ぶという選択的な認識を責めるつもりです;-)私はもう一度それを見るので、Mike Friedmanが著者であることは明らかです。 –

関連する問題