目錄

名稱

Exporter - 實作模組的預設匯入方法

語法

在模組 YourModule.pm

package YourModule;
use Exporter 'import';
our @EXPORT_OK = qw(munge frobnicate);  # symbols to export on request

package YourModule;
require Exporter;
our @ISA = qw(Exporter);  # inherit all of Exporter's methods
our @EXPORT_OK = qw(munge frobnicate);  # symbols to export on request

package YourModule;
use parent 'Exporter';  # inherit all of Exporter's methods
our @EXPORT_OK = qw(munge frobnicate);  # symbols to export on request

在其他想要使用 YourModule 的檔案中

use YourModule qw(frobnicate);      # import listed symbols
frobnicate ($left, $right)          # calls YourModule::frobnicate

請參閱 "良好實務",以瞭解您會想在現代 Perl 程式碼中使用的部分變體。

說明

Exporter 模組實作 import 方法,讓模組可以將函式和變數匯出到使用者的命名空間。許多模組使用 Exporter,而不是實作自己的 import 方法,因為 Exporter 提供高度彈性的介面,並針對常見情況進行最佳化實作。

處理模組的 use 陳述式時,Perl 會自動呼叫 import 方法。模組和 use 記錄在 perlfuncperlmod 中。瞭解模組的概念以及 use 陳述式的運作方式,對於瞭解 Exporter 非常重要。

如何匯出

模組中的陣列 @EXPORT@EXPORT_OK 分別包含預設會匯出到使用者名稱空間的符號清單,或使用者可以要求匯出的符號清單。這些符號可以代表函式、純量、陣列、雜湊或型別全域變數。這些符號必須以完整名稱提供,但函式前面的&符號是選用的,例如:

our @EXPORT    = qw(afunc $scalar @array);   # afunc is a function
our @EXPORT_OK = qw(&bfunc %hash *typeglob); # explicit prefix on &bfunc

如果您只匯出函式名稱,建議省略&符號,因為這樣實作會比較快。

選擇要匯出的內容

不要 匯出方法名稱!

不要 在沒有充分理由的情況下預設匯出其他任何內容!

匯出會污染模組使用者的命名空間。如果您必須匯出,請嘗試優先使用 @EXPORT_OK,而不是 @EXPORT,並避免使用簡短或常見的符號名稱,以降低名稱衝突的風險。

一般來說,任何未匯出的內容仍然可以使用 YourModule::item_name (或 $blessed_ref->method) 語法從模組外部存取。根據慣例,您可以在名稱前面加上底線,非正式地表示它們是「內部」的,不供公開使用。

(實際上,您可以透過說

my $subref = sub { ... };
$subref->(@args);            # Call it as a function
$obj->$subref(@args);        # Use it as a method

來取得私有函式。但是,如果您將它們用於方法,則必須自己找出如何讓繼承運作。)

一般而言,如果模組嘗試成為物件導向,則不輸出任何內容。如果它只是一組函式,則@EXPORT_OK任何內容,但請謹慎使用@EXPORT。對於函式和方法名稱,請優先使用裸字,而不是使用加上&符號作為前置詞的名稱來輸出清單。

其他模組設計指南可以在perlmod中找到。

如何匯入

在想要使用模組的其他檔案中,有三個基本方法可以載入模組並匯入其符號

use YourModule;

這會將 YourModule 的@EXPORT中的所有符號匯入use陳述式的名稱空間中。

use YourModule ();

這會導致 perl 載入模組,但不會匯入任何符號。

use YourModule qw(...);

這只會匯入呼叫者列出的符號到其名稱空間中。所有列出的符號都必須在您的@EXPORT@EXPORT_OK中,否則會發生錯誤。Exporter 的進階匯出功能是以這種方式存取的,但清單項目在語法上與符號名稱不同。

除非您想要使用其進階功能,否則這可能是您使用 Exporter 所需要知道的全部內容。

進階功能

特殊化匯入清單

如果匯入清單中的任何項目以 !、: 或 / 開頭,則清單會被視為一系列規格,這些規格會新增到要匯入的名稱清單中或從中刪除。它們會從左到右處理。規格的格式為

[!]name         This name only
[!]:DEFAULT     All names in @EXPORT
[!]:tag         All names in $EXPORT_TAGS{tag} anonymous array
[!]/pattern/    All names in @EXPORT and @EXPORT_OK which match

一個前導的 ! 表示應該從要匯入的名稱清單中刪除相符的名稱。如果第一個規範是刪除,則會視為在前面加上 :DEFAULT。如果您只想匯入額外的名稱,除了預設集之外,您仍然需要明確包含 :DEFAULT。

例如,Module.pm 定義

our @EXPORT      = qw(A1 A2 A3 A4 A5);
our @EXPORT_OK   = qw(B1 B2 B3 B4 B5);
our %EXPORT_TAGS = (T1 => [qw(A1 A2 B1 B2)], T2 => [qw(A1 A2 B3 B4)]);

請注意,您無法在 @EXPORT 或 @EXPORT_OK 中使用標籤。

EXPORT_TAGS 中的名稱也必須出現在 @EXPORT 或 @EXPORT_OK 中。

使用 Module 的應用程式可以說類似

use Module qw(:DEFAULT :T2 !B3 A3);

其他範例包括

use Socket qw(!/^[AP]F_/ !SOMAXCONN !SOL_SOCKET);
use POSIX  qw(:errno_h :termios_h !TCSADRAIN !/^EXIT/);

請記住,大多數模式(使用 //)需要以一個前導 ^ 來錨定,例如,/^EXIT/ 而不是 /EXIT/

您可以說 BEGIN { $Exporter::Verbose=1 } 來查看規範如何處理,以及實際匯入模組中的內容。

不使用 Exporter 的 import 方法匯出

Exporter 有特殊方法 'export_to_level',用於您無法直接呼叫 Exporter 的 import 方法的情況。export_to_level 方法如下所示

    MyPackage->export_to_level(
	$where_to_export, $package, @what_to_export
    );

其中 $where_to_export 是整數,說明要匯出符號的呼叫堆疊有多高,而 @what_to_export 是陣列,說明要匯出哪些符號(通常是 @_)。$package 參數目前未使用。

例如,假設您有一個模組 A,它已經有匯入函式

    package A;

    our @ISA = qw(Exporter);
    our @EXPORT_OK = qw($b);

    sub import
    {
	$A::b = 1;     # not a very useful import method
    }

您想要將符號 $A::b 匯出回呼叫套件 A 的模組。由於 Exporter 透過繼承依賴 import 方法執行,因此 Exporter::import() 將永遠不會被呼叫。相反,請說以下內容

    package A;
    our @ISA = qw(Exporter);
    our @EXPORT_OK = qw($b);

    sub import
    {
	$A::b = 1;
	A->export_to_level(1, @_);
    }

這將匯出符號,比目前套件高一層級 - 即:到使用套件 A 的程式或模組。

注意:在呼叫 export_to_level 之前,請小心不要修改 @_,否則使用您的套件的人會得到非常難以解釋的結果!

不繼承 Exporter 進行匯出

@ISA 中包含 Exporter,您會繼承 Exporter 的 import() 方法,但您也會繼承其他幾個您可能不需要且會使繼承樹複雜化的輔助方法。為避免此情況,您可以執行

package YourModule;
use Exporter qw(import);

這將把 Exporter 自身的 import() 方法匯出到 YourModule。一切都會像以前一樣運作,但您不需要在 @YourModule::ISA 中包含 Exporter。

注意:此功能是在 Exporter 的 5.57 版中引入的,並隨 perl 5.8.3 發布。

模組版本檢查

Exporter 模組會將嘗試從模組匯入數字轉換為呼叫 $module_name->VERSION($value)。這可用於驗證所使用的模組版本是否大於或等於所需版本。

由於歷史原因,Exporter 提供了一個 require_version 方法,它只是委派給 VERSION。最初,在 UNIVERSAL::VERSION 存在之前,Exporter 會呼叫 require_version

由於 UNIVERSAL::VERSION 方法將 $VERSION 數字視為一個簡單的數字值,因此它會將版本 1.10 視為低於 1.9。因此,強烈建議您使用小數位數至少為兩位的數字,例如 1.09。

管理未知符號

在某些情況下,您可能希望防止匯出某些符號。這通常適用於在某些系統上可能不存在函數或常數的擴充功能。

任何無法匯出的符號名稱都應列在 @EXPORT_FAIL 陣列中。

如果模組嘗試匯入任何這些符號,Exporter 會讓模組在產生錯誤之前有機會處理情況。Exporter 會呼叫 export_fail 方法,並提供失敗符號的清單

@failed_symbols = $module_name->export_fail(@failed_symbols);

如果 export_fail 方法傳回一個空清單,則不會記錄任何錯誤,且所有要求的符號都會被匯出。如果傳回的清單不為空,則會為每個符號產生一個錯誤,且匯出會失敗。Exporter 提供一個預設的 export_fail 方法,它只會不變更地傳回清單。

export_fail 方法的用途包括提供一些符號更好的錯誤訊息,以及執行延遲的架構檢查(預設將更多符號放入 @EXPORT_FAIL,然後在有人實際嘗試使用它們時將它們取出,且昂貴的檢查顯示它們可以在該平台上使用)。

標籤處理工具函式

由於列在 %EXPORT_TAGS 中的符號也必須出現在 @EXPORT@EXPORT_OK 中,因此提供兩個工具函式,讓您可以輕鬆地將標籤符號集新增至 @EXPORT@EXPORT_OK

our %EXPORT_TAGS = (foo => [qw(aa bb cc)], bar => [qw(aa cc dd)]);

Exporter::export_tags('foo');     # add aa, bb and cc to @EXPORT
Exporter::export_ok_tags('bar');  # add aa, cc and dd to @EXPORT_OK

任何不是標籤的名稱都會不變更地新增至 @EXPORT@EXPORT_OK,但會觸發警告(使用 -w),以避免拼錯的標籤名稱被靜默新增至 @EXPORT@EXPORT_OK。未來版本可能會將此設為致命錯誤。

產生組合標籤

如果 %EXPORT_TAGS 中存在多個符號類別,通常會建立工具程式「:all」來簡化「use」陳述式。

最簡單的方法是

our  %EXPORT_TAGS = (foo => [qw(aa bb cc)], bar => [qw(aa cc dd)]);

 # add all the other ":class" tags to the ":all" class,
 # deleting duplicates
 {
   my %seen;

   push @{$EXPORT_TAGS{all}},
     grep {!$seen{$_}++} @{$EXPORT_TAGS{$_}} foreach keys %EXPORT_TAGS;
 }

CGI.pm 會建立一個「:all」標籤,其中包含一些(但並非全部)類別。這可以用一個小變更來完成

# add some of the other ":class" tags to the ":all" class,
# deleting duplicates
{
  my %seen;

  push @{$EXPORT_TAGS{all}},
    grep {!$seen{$_}++} @{$EXPORT_TAGS{$_}}
      foreach qw/html2 html3 netscape form cgi internal/;
}

請注意,%EXPORT_TAGS 中的標籤名稱沒有前導的「:」。

AUTOLOADed 常數

許多模組使用 AUTOLOADing 來處理常數子程式,以避免編譯和浪費記憶體在很少使用的值上(有關常數子程式的詳細資訊,請參閱 perlsub)。對此類常數子程式的呼叫在編譯時並未最佳化,因為它們無法在編譯時檢查常數。

即使在編譯時有原型,子程式的程式主體也不存在(它尚未 AUTOLOADed)。perl 需要在編譯時檢查 () 原型和子程式的程式主體,以偵測它是否可以安全地使用常數值取代對該子程式的呼叫。

解決方法是在 BEGIN 區塊中呼叫一次常數

package My ;

use Socket ;

foo( SO_LINGER );  ## SO_LINGER NOT optimized away; called at runtime
BEGIN { SO_LINGER }
foo( SO_LINGER );  ## SO_LINGER optimized away at compile time.

這會強制 SO_LINGERAUTOLOADMy 套件中稍後遇到 SO_LINGER 之前發生。

如果您正在撰寫 AUTOLOADs 的套件,請考慮強制 AUTOLOAD 任何由其他套件明確匯入的常數,或在使用您的套件時通常使用的常數。

良好實務

宣告 @EXPORT_OK 和相關項目

在使用 Exporter 搭配標準的 strictwarnings 實用模組時,需要 our 關鍵字來宣告套件變數 @EXPORT_OK@EXPORT@ISA 等。

our @ISA = qw(Exporter);
our @EXPORT_OK = qw(munge frobnicate);

如果向後相容性對 低於 5.6 的 Perl 很重要,則必須改寫 use vars 陳述式。

use vars qw(@ISA @EXPORT_OK);
@ISA = qw(Exporter);
@EXPORT_OK = qw(munge frobnicate);

安全考量

使用執行時期陳述式(例如 require Exporter 和指派套件變數)有一些注意事項,對於不知情的程式設計師來說,這些注意事項可能非常微妙。例如,這可能會發生在相互遞迴的模組中,這些模組會受到相關建構執行的時間影響。

永遠不必考慮這一點的理想方法是使用 BEGIN 區塊和簡單的匯入方法。因此,"SYNOPSIS" 程式碼的第一部分可以改寫為

package YourModule;

use strict;
use warnings;

use Exporter 'import';
BEGIN {
  our @EXPORT_OK = qw(munge frobnicate);  # symbols to export on request
}

或者,如果您需要繼承自 Exporter

package YourModule;

use strict;
use warnings;

BEGIN {
  require Exporter;
  our @ISA = qw(Exporter);  # inherit all of Exporter's methods
  our @EXPORT_OK = qw(munge frobnicate);  # symbols to export on request
}

BEGIN 將確保載入 Exporter.pm 以及指派給 @ISA@EXPORT_OK 的動作立即發生,就像 use 一樣,不留任何空間讓事情出錯或完全錯誤。

關於載入 Exporter 和繼承,可以使用 baseparent 等模組來提供替代方案。

use base qw(Exporter);
# or
use parent qw(Exporter);

這些陳述中的任何一個都可以很好地取代 BEGIN { require Exporter; our @ISA = qw(Exporter); },並具有相同的編譯時間效果。基本區別在於 base 程式碼與宣告的 fields 互動,而 parent 是舊版 base 程式碼的精簡版本,僅用於建立 IS-A 關係。

如需更多詳細資訊,請參閱 baseparent 的文件和程式碼。

另一個徹底解決執行時間與編譯時間陷阱的方法是使用 Exporter::Easy,它是 Exporter 的包裝器,允許在 use 陳述中一次 gulp 處理所有樣板程式碼。

use Exporter::Easy (
    OK => [ qw(munge frobnicate) ],
);
# @ISA setup is automatic
# all assignments happen at compile time

不要匯出的內容

您已在 "選擇要匯出的內容" 中收到警告,不要匯出

此清單中還有一項要新增。不要匯出變數名稱。僅因為 Exporter 允許您這樣做,並不表示您應該這樣做。

@EXPORT_OK = qw($svar @avar %hvar); # DON'T!

匯出變數並非好主意。它們可以在幕後變更,從而導致難以追蹤和修復的可怕遠距離效應。相信我:它們不值得。

相反,為了提供設定/取得類別範圍設定的能力,最好提供存取器作為子常式或類別方法。

另請參閱

Exporter 絕對不是唯一具有符號匯出器功能的模組。在 CPAN 中,您可能會找到一堆。有些較為輕量級。有些提供進階 API 和功能。選擇符合您需求的模組。以下是此類模組的範例清單。

Exporter::Easy
Exporter::Lite
Exporter::Renaming
Exporter::Tidy
Sub::Exporter / Sub::Installer
Perl6::Export / Perl6::Export::Attrs

授權

此函式庫是免費軟體。您可以在與 Perl 相同的條款下重新散布或修改它。