2009-06-12 90 views
45

一個鮮爲人知的內置Perl特性是屬性。然而,官方documentation做了一個相當糟糕的工作,引入新概念。與此同時,像Catalyst這樣的框架廣泛使用屬性,這似乎使許多事情變得更容易。由於在不知道含義的情況下使用某些內容會讓人感到厭倦,所以我想知道細節。語法方面,它們看起來像Python的裝飾器,但文檔意味着更簡單。Perl方法屬性如何工作?

你能解釋一下(如果可能的話,用真實世界的例子)什麼屬性是好的,門後會發生什麼?

回答

36

你說得對,這方面的文檔不是很清楚,特別是因爲屬性並不那麼複雜。如果定義了一個子程序的屬性,就像這樣:

sub some_method :Foo { } 

Perl將在編譯程序時(這很重要)尋找魔術子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 

綜上所述,屬性不會做很多這在另一方面使得它們非常靈活:您可以將它們作爲真正的「屬性」(如本例所示),實現了某些東西像裝飾者(見Sinan's answer),或爲了你自己的迂迴目的。

5

屬性是如果你不知道如何使用它們的事情之一,你不應該打擾他們。我曾經做過一個database_method屬性,向系統表明在進入該方法之前將要求記錄集,並且該方法知道它的主要輸入來自它所對應的存儲過程。

我正在使用屬性來包裝與該數據的實際,指定的操作。因此,其中一個看起來很有用的想法是用間接方式來包裝方法,但在不重寫它的情況下更難做出調用者的工作。最後,它作爲一個「專家專用」功能顯而易見,並且需要支持通過奧術內部進行追蹤 - 如果您在perl-shop中編寫Perl,則需要避免這種情況。


人們可能想投我失望,但我從文章引用思南採取:

注意事項

雖然這是一個強大的技術,它並不完美。 該代碼不會正確包裝匿名子例程它不一定會傳播調用上下文到包裝函數。此外,使用這種技術會顯着增加程序在運行時必須執行的子例程調度的次數。根據程序的複雜程度,這可能會顯着增加調用堆棧的大小。如果致盲速度是主要的設計目標,那麼這種策略可能不適合你。

這些顯著缺點,除非你願意重寫caller。我並不關心「致盲速度」,而且我也樂於嘗試着重寫caller以繞過任何註冊自己爲「DO_NOT_REPORT」的子例程 - 但是我有一些編碼的愚蠢行爲,還沒有被打敗我。

即使這篇文章也承認這個功能是多麼不健全,並且包含這個警告。告訴我什麼時候使用時髦,晦澀的功能是個好主意?通常情況下,人們最終會放入UNIVERSAL命名空間以避免繼承問題。

(但是,如果你認爲這是一個不好回答,只是一個downvote會給我一個同齡人的壓力徽章:d)

+2

確實如此,但這些注意事項僅適用於使用屬性的特定方式,即包裝原始方法。在大多數情況下,使用屬性(催化劑等),它們只是用於標記(我認爲),根本沒有問題。 – trendels 2009-06-12 16:11:24

+0

儘管這是作爲最清晰的Perl教程之一的主要用途之一(本來可以節省一些時間的)。我不得不承認,我還沒有登記到催化劑。 – Axeman 2009-06-12 16:39:14

11
+1

@brian感謝您的糾正。我要指責選擇性的認知,從那個頁面上的作者列表中選擇你的名字;-)現在我再看一遍,很明顯Mike Friedman是作者。 – 2009-06-12 23:50:16