2010-11-27 32 views

回答

1

簡單且最可能正確的答案是,導出給定函數的最後一個模塊將是使用其功能的模塊。

當一個函數(或其他符號)被導出時,接收包的符號表被修改。因此,如果兩個導​​入函數對錶進行更改,則最後的更改將保留。

回想一下,use Foo;或多或少相當於BEGIN { require Foo; Foo->import if Foo->can(import); }

由於import只是一個帶有特殊名稱的子程序,唯一的限制是J. Random Hacker的扭曲想象。 import可以是任何代碼。它可以做任何事或不做任何事。例如,它可能包含邏輯以確保沒有已定義的函數被覆蓋。

但是,除了任何古怪,最後加載將是使用中的一個。


爲100%技術上正確,use Foo;相當於BEGIN { require Foo; Foo->import; }

除了這個代碼不能像你所期望的那樣工作。

確切的代碼與我的例子不同。

上面的代碼做了什麼與方法解析在Perl中的工作方式有關。

通常,到Foo->some_sub一個呼叫,其中some_sub不存在和AUTOLOAD功能Foo定義,some_sub將由AUTOLOAD函數處理。 import有一個特殊情況,可免除AUTOLOAD檢查。見perlsub on Autoloading

AUTOLOAD已經(或確實沒有)被檢查後,我們檢查繼承。對於原包裝@ISA中的每個項目,重複匹配功能或AUTOLOAD函數的相同檢查。這是一個遞歸的過程,包括父母的父母等等,直到整個繼承樹被選中 - @ISA被檢查從左到右,深度第一順序。所有這一次AUTOLOAD例外都已到位,它將被跳過import

最終,如果找不到匹配的方法,我們就回到UNIVERSAL通用基類。如果該函數存在於UNIVERSAL中,則調用該函數,否則會拋出一個異常,表示無法找到該函數。

因此,在我們的Foo->import;調用的情況下,調用UNIVERSAL::import來處理作業。那麼,這個功能是做什麼的?

在Perl 5.12.2的代碼如下所示:

sub import { 
    return unless $_[0] eq __PACKAGE__; 
    return unless @_ > 1; 
    require warnings; 
    warnings::warnif(
     'deprecated', 
     'UNIVERSAL->import is deprecated and will be removed in a future perl', 
    ); 
    goto &Exporter::import; 
} 

注意,功能做的第一件事是擺脫困境,如果它不叫上UNIVERSAL

現在的一切,我說的是真的,只要你不要做幾件事情:

  • 覆蓋的方法解析順序。如果你這樣做,然後方法的解決將發生,但你已經定義它。無論我們是否達到UNIVERSAL,或者什麼時候完全在空中,並受到你的突發奇想。

  • 覆蓋UNIVERSAL::import。你可以猴子修補程序UNIVERSAL :: import來做任何你想要的。再次,這將如何表現完全取決於你的心血來潮。

因此,上面給出的半等效代碼僅僅是發生什麼的簡寫。我認爲這會更容易理解,因爲它不需要知道Perl如何處理事情的很多細節,但它不是100%等同於真正發生的事情。做出意想不到的事情打破了等值。

凡我代碼完全等效代碼

變化。此外,我的代碼調用Foo->can一般回落到UNIVERSAL::cancan無法在正常的事件鏈中調用。當考慮到Perl中的can問題時,這會變得特別多毛。

  • can可能會被繼承圖中的任何類所覆蓋或重新實現。其中can被調用受制於方法解析順序。多重繼承的所有問題都適用於此。
  • can未看到自動加載功能。由於自動加載不適用於導入,因此這可能不是什麼大不了的事情。問題是,如果您使用自動加載,則認爲將can重新考慮在內是一種很好的做法。所以,這會增加上述問題。最好的方法是使用非核心模塊NEXT來啓用重新分派方法,以便可以由鏈中的每個模塊處理。不幸的是,這很少完成。

結論

所有這是很多來啃的地獄。

你可以接受我的速記,知道在某些情況下,它不完全正確。

或者你可以接受實際的代碼,它有自己的一套例外。

無論哪種方式,如果你突破了任何一個例子的表面,都有一些細微的問題需要解決。

+2

無需檢查是否可以導入,如果在Foo – MkV 2010-11-27 11:43:26

相關問題