屬性 - 取得/設定子常式或變數屬性
sub foo : method ;
my ($x,@y,%z) : Bent = 1;
my $s = sub : method { ... };
use attributes (); # optional, to get subroutine declarations
my @attrlist = attributes::get(\&foo);
use attributes 'get'; # import the attributes::get subroutine
my @attrlist = get \&foo;
子常式宣告和定義可以選擇與它們關聯屬性清單。(變數 my
宣告也可以,但請參閱以下警告。)Perl 透過將一些關於呼叫位置和宣告事項的資訊與屬性清單傳遞給此模組來處理這些宣告。特別是,上述第一個範例等於下列內容
use attributes __PACKAGE__, \&foo, 'method';
語法中的第二個範例執行類似下列內容
use attributes ();
my ($x,@y,%z);
attributes::->import(__PACKAGE__, \$x, 'Bent');
attributes::->import(__PACKAGE__, \@y, 'Bent');
attributes::->import(__PACKAGE__, \%z, 'Bent');
($x,@y,%z) = 1;
是的,那是很多擴充。
警告:變數的屬性宣告仍在演進中。此類宣告的語意和介面可能會在未來版本中變更。它們的存在是為了實驗語意應為何。請勿依賴此功能的目前實作。
目前只有少數屬性由 Perl 本身處理(或直接由這個模組處理,視你的觀點而定)。不過,套件特定屬性是由擴充機制允許的。(請參閱以下的 "套件特定屬性處理"。)
子常式屬性的設定發生在編譯時間。our
宣告中的變數屬性也會在編譯時間套用。不過,my
變數會在執行時間套用它們的屬性。這表示你必須到達 my
的執行時間元件,這些屬性才會套用。例如
my $x : Bent = 42 if 0;
既不會將 42 指派給 $x,也不會將 Bent
屬性套用至變數。
嘗試設定未識別的屬性會導致致命錯誤。(錯誤可以捕捉,但仍會停止該 eval
中的編譯。)設定名稱全為小寫字母且並非內建屬性的屬性(例如「foo」)會導致警告,並顯示 -w 或 use warnings 'reserved'
。
import
的作用說明中提到
sub foo : method;
等同於
use attributes __PACKAGE__, \&foo, 'method';
您可能知道,這會在編譯時呼叫 attributes
的 import
函數,並使用下列參數:'attributes'、呼叫者的套件名稱、程式碼參考和 'method'。
attributes->import( __PACKAGE__, \&foo, 'method' );
所以您想知道 import
實際上做了什麼嗎?
首先,import
會取得第三個參數的類型(此處為 'CODE')。attributes.pm
會檢查呼叫者的命名空間中是否有稱為 MODIFY_<reftype>_ATTRIBUTES
的子常式(此處為 'main')。在此情況下,需要 MODIFY_CODE_ATTRIBUTES
子常式。接著會呼叫此方法,以檢查您是否使用了「錯誤屬性」。此範例中的子常式呼叫會如下所示
MODIFY_CODE_ATTRIBUTES( 'main', \&foo, 'method' );
MODIFY_<reftype>_ATTRIBUTES
必須傳回所有「錯誤屬性」的清單。如果存在任何錯誤屬性,import
會產生 croak。
(請參閱下方「套件特定屬性處理」)
以下是子常式的內建屬性
表示所引用的子常式為有效的 lvalue,且可以指派給它。子常式必須傳回可修改的值,例如純量變數,如 perlsub 中所述。
此模組允許您在已定義的子常式上設定此屬性。對於 Perl 子常式(XSUB 也可以),它可能會或可能不會執行您要執行的動作,視子常式內的程式碼而定,且詳細資訊可能會在未來的 Perl 版本中變更。您可能會遇到 lvalue 內容未正確傳播至子常式的問題,甚至可能會遇到斷言失敗。因此,如果已啟用警告,系統會發出警告。換句話說,只有在您確實知道自己在做什麼時,才應該執行此動作。您已受到警告。
表示所引用的子常式為方法。如此標記的子常式不會觸發「不明確呼叫已解析為 CORE::%s」警告。
「prototype」屬性是指定子程式中原型的一種替代方法。所需的原型在括號內。
屬性中的原型會在子程式的原型之後立即指派給子程式,這表示如果兩者同時宣告,傳統定義的原型會被忽略。換句話說,sub foo($$) : prototype(@) {}
與 sub foo(@){}
沒有區別。
如果啟用 illegalproto 警告,此屬性中宣告的原型會在編譯時進行健全性檢查。
這個實驗性質的屬性在 Perl 5.22 中引入,只適用於匿名子常式。它會在評估 sub
運算式時立即呼叫子常式。會擷取回傳值並將其轉換為常數子常式。
以下是變數的內建屬性
表示在與 threads 和 threads::shared 模組一起使用時,可以跨不同執行緒共用所參照的變數。
載入此模組後,可供一般使用下列子常式
此常式預期一個參數,即子常式或變數的參照。它會回傳屬性清單,可能為空。如果傳遞無效的引數,它會使用 die()(透過 Carp::croak)引發致命例外。如果它可以找到適當的套件名稱來查詢類別方法,它會在回傳清單中包含 FETCH_type_ATTRIBUTES
呼叫的結果,如以下的「套件特定屬性處理」所述。否則,只會回傳 內建屬性。
此常式預期一個參數,即子常式或變數的參照。它會回傳所參照變數的內建類型,忽略它可能已被祝福進入的任何套件。這對於決定構成以下「套件特定屬性處理」中方法名稱一部分的 type 值很有用。
請注意,這些常式在預設情況下不會匯出。
警告:此處描述的機制仍處於實驗階段。請勿依賴目前的實作。特別是,沒有提供將套件屬性套用於用作封閉的子常式「複製」副本的規定。(請參閱 perlref 中的「建立參照」,以取得有關封閉的資訊。)套件特定屬性處理在未來的版本中可能會發生不相容的變更。
當宣告中存在屬性清單時,會檢查適當的套件(或其 @ISA 繼承樹)中是否存在屬性「修改」處理常式。類似地,當對有效的參照呼叫 attributes::get
時,會檢查適當的屬性「擷取」處理常式。請參閱 「範例」,以了解「適當的套件」決定如何運作。
處理常式名稱是根據宣告的變數或傳遞參照的底層類型。由於這些屬性與子常式或變數宣告相關,因此這會故意忽略被祝福進入某個套件的任何可能性。因此,子常式宣告使用「CODE」作為其 type,而即使是祝福的雜湊參照也使用「HASH」作為其 type。
用於修改和擷取的類別方法如下
此方法呼叫時有兩個參數:相關套件名稱,以及要取得套件定義屬性的變數或子常式的參考。預期的傳回值是關聯屬性的清單。此清單可能為空。
此方法呼叫時有兩個固定參數,後面接著相關宣告的屬性清單。兩個固定參數是相關套件名稱和宣告的子常式或變數的參考。預期的傳回值是此處理常式未辨識的屬性清單。請注意,這允許衍生類別委派呼叫給其基底類別,然後只檢查基底類別尚未為其處理的屬性。
目前在宣告處理期間呼叫此方法。特別是,這表示子常式參考可能是未定義的子常式,即使此宣告實際上是定義的一部分。
在空套件宣告 package ;
範圍內,針對未套用祝福的變數參考呼叫 attributes::get()
,不會提供任何起始套件名稱供「取得」方法查詢。因此,此情況不會導致套件定義屬性的方法呼叫。已命名子常式知道它屬於哪個符號表項目(或原本屬於哪個),且會使用對應的套件。匿名子常式知道它編譯到哪個套件名稱(除非它也使用空套件宣告編譯),因此會使用該套件名稱。
屬性清單是屬性規格的序列,以空白或冒號(可加上空白)分隔。每個屬性規格都是一個簡單名稱,後面可選擇加上括號參數清單。如果存在此類參數清單,則會根據 q()
算子的規則掃描過去。(請參閱 perlop 中的「引號和類引號算子」。)不過,參數清單會照原本找到的樣子傳遞,而不是像 q()
那樣。
語法上有效的屬性清單範例
switch(10,foo(7,3)) : expensive
Ugly('\(") :Bad
_5x5
lvalue method
語法上無效的屬性清單範例(附註解)
switch(10,foo() # ()-string not balanced
Ugly('(') # ()-string not balanced
5x5 # "5x5" not a valid identifier
Y2::north # "Y2::north" not a simple identifier
foo + bar # "+" neither a colon nor whitespace
無。
常式 get
和 reftype
可匯出。
:ALL
標籤將取得上述所有匯出。
以下是語法上有效的宣告範例,並註解它們如何內部解析成 perl 的 use attributes
呼叫。這些範例主要用於查看如何為封包定義的屬性找到「適當的封包」,以進行可能的函式查找。
程式碼
package Canine;
package Dog;
my Canine $spot : Watchful ;
效果
use attributes ();
attributes::->import(Canine => \$spot, "Watchful");
程式碼
package Felis;
my $cat : Nervous;
效果
use attributes ();
attributes::->import(Felis => \$cat, "Nervous");
程式碼
package X;
sub foo : lvalue ;
效果
use attributes X => \&foo, "lvalue";
程式碼
package X;
sub Y::x : lvalue { 1 }
效果
use attributes Y => \&Y::x, "lvalue";
程式碼
package X;
sub foo { 1 }
package Y;
BEGIN { *bar = \&X::foo; }
package Z;
sub Y::bar : lvalue ;
效果
use attributes X => \&X::foo, "lvalue";
最後這個範例純粹是為了完整性。您不應該嘗試修改不屬於您自己的封包中的屬性。
sub MODIFY_CODE_ATTRIBUTES {
my ($class,$code,@attrs) = @_;
my $allowed = 'MyAttribute';
my @bad = grep { $_ ne $allowed } @attrs;
return @bad;
}
sub foo : MyAttribute {
print "foo\n";
}
這個範例會執行。在編譯時會呼叫 MODIFY_CODE_ATTRIBUTES
。在那個子常式中,我們會檢查是否有任何屬性是不允許的,並傳回這些「不良屬性」的清單。
由於我們傳回一個空清單,因此一切正常。
sub MODIFY_CODE_ATTRIBUTES {
my ($class,$code,@attrs) = @_;
my $allowed = 'MyAttribute';
my @bad = grep{ $_ ne $allowed }@attrs;
return @bad;
}
sub foo : MyAttribute Test {
print "foo\n";
}
這個範例會在編譯時中止,因為我們使用了不允許的屬性「Test」。MODIFY_CODE_ATTRIBUTES
傳回一個清單,其中包含一個元素(「Test」)。
"透過 my()" 在 perlsub 中建立私人變數 和 "子常式屬性" 在 perlsub 中,以取得基本宣告的詳細資料;"use" 在 perlfunc 中,以取得一般呼叫機制的詳細資料。