簽名檔#

介面定義檔 (.pyf) 是您微調 Python 和 Fortran 之間介面的方式。簽名檔(.pyf 檔案)的語法規範以 Fortran 90/95 語言規範為模型。幾乎所有 Fortran 標準結構都被理解,無論是自由格式還是固定格式(回想一下 Fortran 77 是 Fortran 90/95 的子集)。F2PY 對 Fortran 90/95 語言規範引入了一些擴展,以幫助設計 Fortran 到 Python 的介面,使其更「Pythonic」。

簽名檔可能包含任意 Fortran 程式碼,因此任何 Fortran 90/95 程式碼都可以被視為簽名檔。F2PY 會靜默忽略與建立介面無關的 Fortran 結構。但是,這也意味著語法錯誤不會被 F2PY 捕獲,只會在建置函式庫時被捕獲。

注意

目前,F2PY 可能會因某些有效的 Fortran 結構而失敗。如果發生這種情況,您可以查看 NumPy GitHub issue tracker 以尋找可能的回避方法或進行中的想法。

一般來說,簽名檔的內容是區分大小寫的。當掃描 Fortran 程式碼以產生簽名檔時,F2PY 會自動將所有字母轉換為小寫,除非在多行區塊中或使用 --no-lower 選項時。

簽名檔的語法如下所示。

簽名檔語法#

Python 模組區塊#

一個簽名檔可能包含一個(建議)或多個 python module 區塊。python module 區塊描述了 F2PY 產生的 Python/C 擴充模組 <modulename>module.c 的內容。

警告

例外:如果 <modulename> 包含子字串 __user__,則對應的 python module 區塊描述了回呼函數的簽名(請參閱 回呼引數)。

python module 區塊具有以下結構

python module <modulename>
  [<usercode statement>]...
  [
  interface
    <usercode statement>
    <Fortran block data signatures>
    <Fortran/C routine signatures>
  end [interface]
  ]...
  [
  interface
    module <F90 modulename>
      [<F90 module data type declarations>]
      [<F90 module routine signatures>]
    end [module [<F90 modulename>]]
  end [interface]
  ]...
end [python module [<modulename>]]

這裡的方括號 [] 表示可選部分,點號 ... 表示前一個部分的一個或多個。因此,[]... 應理解為前一個部分的零個或多個。

Fortran/C 常式簽名#

Fortran 常式簽名的結構如下

[<typespec>] function | subroutine <routine name> \
              [ ( [<arguments>] ) ] [ result ( <entityname> ) ]
  [<argument/variable type declarations>]
  [<argument/variable attribute statements>]
  [<use statements>]
  [<common block statements>]
  [<other statements>]
end [ function | subroutine [<routine name>] ]

從 Fortran 常式簽名,F2PY 產生具有以下簽名的 Python/C 擴充函數

def <routine name>(<required arguments>[,<optional arguments>]):
     ...
     return <return variables>

Fortran 區塊資料的簽名結構如下

block data [ <block data name> ]
  [<variable type declarations>]
  [<variable attribute statements>]
  [<use statements>]
  [<common block statements>]
  [<include statements>]
end [ block data [<block data name>] ]

類型宣告#

<引數/變數 類型宣告> 部分的定義是

<typespec> [ [<attrspec>] :: ] <entitydecl>

其中

<typespec> := byte | character [<charselector>]
           | complex [<kindselector>] | real [<kindselector>]
           | double complex | double precision
           | integer [<kindselector>] | logical [<kindselector>]

<charselector> := * <charlen>
               | ( [len=] <len> [ , [kind=] <kind>] )
               | ( kind= <kind> [ , len= <len> ] )
<kindselector> := * <intlen> | ( [kind=] <kind> )

<entitydecl> := <name> [ [ * <charlen> ] [ ( <arrayspec> ) ]
                      | [ ( <arrayspec> ) ] * <charlen> ]
                     | [ / <init_expr> / | = <init_expr> ] \
                       [ , <entitydecl> ]

  • <attrspec> 是以逗號分隔的 屬性列表;

  • <arrayspec> 是以逗號分隔的維度界限列表;

  • <init_expr>C 運算式

  • <intlen> 可能為負整數,用於 integer 類型規範。在這種情況下,integer*<negintlen> 表示無符號 C 整數;

如果引數沒有 <引數 類型宣告>,則其類型透過將 implicit 規則應用於其名稱來確定。

陳述式#

屬性陳述式#

<引數/變數 屬性陳述式> 類似於 <引數/變數 類型宣告>,但沒有 <typespec>

屬性陳述式不能包含其他屬性,且 <entitydecl> 只能是名稱列表。有關 F2PY 可以使用的屬性的更多詳細資訊,請參閱 屬性

Use 陳述式#

  • <use statement> 部分的定義是

    use <modulename> [ , <rename_list> | , ONLY : <only_list> ]
    

    其中

    <rename_list> := <local_name> => <use_name> [ , <rename_list> ]
    
  • 目前,F2PY 僅使用 use 陳述式來連結回呼模組和 external 引數(回呼函數)。請參閱 回呼引數

Common 區塊陳述式#

  • <common block statement> 部分的定義是

    common / <common name> / <shortentitydecl>
    

    其中

    <shortentitydecl> := <name> [ ( <arrayspec> ) ] [ , <shortentitydecl> ]
    
  • 如果 python module 區塊包含兩個或多個同名的 common 區塊,則會附加來自額外宣告的變數。<shortentitydecl> 中變數的類型使用 <引數 類型宣告> 定義。請注意,對應的 <引數 類型宣告> 可能包含陣列規範;那麼這些不需要在 <shortentitydecl> 中指定。

其他陳述式#

  • <other statement> 部分指的是上面未描述的任何其他 Fortran 語言結構。F2PY 忽略它們中的大多數,除了以下

    • call 陳述式和 external 引數的函數呼叫(請參閱 關於 external 引數的更多詳細資訊);

    • include 陳述式
      include '<filename>'
      include "<filename>"
      

      如果檔案 <filename> 不存在,則 include 陳述式會被忽略。否則,檔案 <filename> 會被包含到簽名檔中。include 陳述式可以在簽名檔的任何部分使用,也可以在 Fortran/C 常式簽名區塊之外使用。

    • implicit 陳述式
      implicit none
      implicit <list of implicit maps>
      

      其中

      <implicit map> := <typespec> ( <list of letters or range of letters> )
      

      如果變數未使用 <變數 類型宣告> 定義,則隱含規則用於確定變數的類型規範(從其名稱的首字母)。預設隱含規則由下式給出

      implicit real (a-h,o-z,$_), integer (i-m)
      
    • entry 陳述式
      entry <entry name> [([<arguments>])]
      

      F2PY 使用常式區塊的簽名為所有 entry 名稱產生包裝器。

      注意

      entry 陳述式可用於描述任意副程式或函數的簽名,允許 F2PY 從僅一個常式區塊簽名產生多個包裝器。這樣做有一些限制:不能使用 fortranname,只有在對所有 entry 常式都有效時才能使用 callstatementcallprotoargument 等。

F2PY 陳述式#

此外,F2PY 引入了以下陳述式

threadsafe

在呼叫 Fortran/C 函數的周圍使用 Py_BEGIN_ALLOW_THREADS .. Py_END_ALLOW_THREADS 區塊。

callstatement <C-expr|多行 區塊>

將 F2PY 產生的對 Fortran/C 函數的呼叫陳述式替換為 <C-expr|多行 區塊>。包裝的 Fortran/C 函數可用作 (*f2py_func)

要引發例外,請在 <C-expr|多行 區塊> 中設定 f2py_success = 0

callprotoargument <C-typespecs>

當使用 callstatement 陳述式時,F2PY 可能不會為 Fortran/C 函數產生正確的原型(因為 <C-expr> 可能包含函數呼叫,並且 F2PY 無法確定正確的原型應該是什麼)。

透過此陳述式,您可以明確指定對應原型的引數

extern <return type> FUNC_F(<routine name>,<ROUTINE NAME>)(<callprotoargument>);
fortranname [<實際 Fortran/C 常式 名稱>]

F2PY 允許對給定的 Fortran/C 函數使用任意的 <常式 名稱>。然後,此陳述式用於 <實際 Fortran/C 常式 名稱>

如果 fortranname 陳述式未使用 <實際 Fortran/C 常式 名稱>,則會產生虛擬包裝器。

usercode <多行 區塊>

當在 python module 區塊內使用時,給定的 C 程式碼將插入到產生的 C/API 原始碼中,緊接在包裝器函數定義之前。

在這裡,您可以定義任意 C 函數,用於初始化可選引數。

例如,如果在 python module 區塊內使用兩次 usercode,則第二個多行區塊將在外部常式的定義之後插入。

當在 <常式 簽名> 內使用時,給定的 C 程式碼將插入到對應的包裝器函數中,緊接在變數宣告之後,但在任何 C 陳述式之前。因此,usercode 後續可以包含宣告和 C 陳述式。

當在第一個 interface 區塊內使用時,給定的 C 程式碼將插入到擴充模組的初始化函數的末尾。這就是如何修改擴充模組字典的方式,並且有許多用例;例如,定義額外的變數。

pymethoddef <多行 區塊>

這是一個多行區塊,將插入到模組方法 PyMethodDef 陣列的定義中。它必須是以逗號分隔的 C 陣列列表(有關詳細資訊,請參閱 擴充與嵌入 Python 文件)。pymethoddef 陳述式只能在 python module 區塊內使用。

屬性#

以下屬性可供 F2PY 使用。

optional

對應的引數會移動到 <可選引數> 列表的末尾。可選引數的預設值可以透過 <init_expr> 指定(請參閱 entitydecl 定義

注意

  • 預設值必須作為有效的 C 運算式給出。

  • 每當使用 <init_expr> 時,F2PY 會自動設定 optional 屬性。

  • 對於可選的陣列引數,其所有維度都必須有界。

required

具有此屬性的對應引數被視為強制性的。這是預設值。required 僅應在需要禁用使用 <init_expr> 時的自動 optional 設定時指定。

如果 Python None 物件用作必要引數,則該引數被視為可選的。也就是說,在陣列引數的情況下,會分配記憶體。如果給定 <init_expr>,則會執行對應的初始化。

dimension(<arrayspec>)

對應的變數被視為具有 <arrayspec> 中給定的維度的陣列。

intent(<intentspec>)

這指定了對應引數的「意圖」。<intentspec> 是以下鍵的逗號分隔列表

  • in

    對應的引數被視為僅輸入。這表示引數的值會傳遞給 Fortran/C 函數,並且該函數預期不會更改此引數的值。

  • inout

    對應的引數標記為輸入/輸出或作為原地輸出引數。intent(inout) 引數只能是 連續 NumPy 陣列(在 Fortran 或 C 意義上),具有正確的類型和大小。後者與 NumPy 中使用的預設連續概念一致,並且僅在使用 intent(c) 時有效。F2PY 預設採用 Fortran 連續引數。

    注意

    通常不建議使用 intent(inout),因為它可能會導致意外的結果。例如,使用 intent(inout) 的純量引數被假定為陣列物件,以便使原地更改生效。請改用 intent(in,out)

    另請參閱 intent(inplace) 屬性。

  • inplace

    對應的引數被視為輸入/輸出或原地輸出引數。intent(inplace) 引數必須是大小正確的 NumPy 陣列。如果陣列的類型不「正確」或陣列不連續,則陣列將會原地修改以修正類型並使其連續。

    注意

    通常也不建議使用 intent(inplace)

    例如,當從 intent(inplace) 引數中取得切片時,原地更改後,切片的資料指標可能會指向未分配的記憶體區域。

  • out

    對應的引數被視為傳回變數。它會附加到 <傳回變數> 列表中。使用 intent(out) 會自動設定 intent(hide),除非也指定了 intent(in)intent(inout)

    預設情況下,傳回的多維陣列是 Fortran 連續的。如果使用 intent(c) 屬性,則傳回的多維陣列是 C 連續的。

  • hide

    對應的引數會從必要或可選引數列表中移除。通常 intent(hide)intent(out) 一起使用,或者當 <init_expr> 完全決定引數的值時,如下例所示

    integer intent(hide),depend(a) :: n = len(a)
    real intent(in),dimension(n) :: a
    
  • c

    對應的引數被視為 C 純量或 C 陣列引數。對於純量引數的情況,其值作為 C 純量引數傳遞給 C 函數(回想一下 Fortran 純量引數實際上是 C 指標引數)。對於陣列引數,包裝器函數假定將多維陣列視為 C 連續陣列。

    無論包裝的函數是在 Fortran 還是 C 中,一維陣列都不需要使用 intent(c)。這是因為 Fortran 和 C 連續性的概念在一維情況下重疊。

    如果 intent(c) 用作陳述式,但沒有實體宣告列表,則 F2PY 會將 intent(c) 屬性新增至所有引數。

    此外,當包裝 C 函數時,必須對 <常式 名稱> 使用 intent(c) 屬性,以停用 Fortran 特定的 F_FUNC(..,..) 巨集。

  • cache

    對應的引數被視為垃圾記憶體。不執行 Fortran 或 C 連續性檢查。僅對陣列引數使用 intent(cache) 才有意義,也應與 intent(hide)optional 屬性結合使用。

  • copy

    確保保留 intent(in) 引數的原始內容。通常與 intent(in,out) 屬性一起使用。F2PY 建立一個可選引數 overwrite_<引數 名稱>,預設值為 0

  • overwrite

    這表示 intent(in) 引數的原始內容可能會被 Fortran/C 函數更改。F2PY 建立一個可選引數 overwrite_<引數 名稱>,預設值為 1

  • out=<新名稱>

    在包裝器函數的 __doc__ 字串中,將傳回的名稱替換為 <新名稱>

  • callback

    建構一個外部函數,適用於從 Fortran 呼叫 Python 函數。intent(callback) 必須在對應的 external 陳述式之前指定。如果「引數」不在引數列表中,則會將其新增到 Python 包裝器,但僅透過初始化外部函數來新增。

    注意

    在 Fortran/C 程式碼假定使用者實作了具有給定原型的函數並將其連結到可執行檔的情況下使用 intent(callback)。如果函數出現在 Fortran 常式的引數列表中,請勿使用 intent(callback)

    在指定了 intent(hide)optional 屬性,並且使用沒有在引數列表中指定回呼引數的包裝器函數的情況下;然後,假定在 F2PY 產生的擴充模組的命名空間中找到回呼函數,使用者可以在其中將其設定為模組屬性。

  • aux

    在 F2PY 產生的包裝器函數中定義一個輔助 C 變數。用於儲存參數值,以便可以在其他變數的初始化運算式中存取它們。

    注意

    intent(aux) 靜默地暗示 intent(c)

以下規則適用

  • 如果未指定 intent(in | inout | out | hide) 中的任何一個,則假定為 intent(in)

    • intent(in,inout)intent(in)

    • intent(in,hide)intent(inout,hide)intent(hide)

    • intent(out)intent(out,hide),除非指定了 intent(in)intent(inout)

  • 如果使用 intent(copy)intent(overwrite),則會引入一個額外的可選引數,名稱為 overwrite_<引數 名稱>,預設值分別為 0 或 1。

    • intent(inout,inplace)intent(inplace)

    • intent(in,inplace)intent(inplace)

    • intent(hide) 停用 optionalrequired

check([<C-booleanexpr>])

透過評估 <C-booleanexpr> 對引數執行一致性檢查;如果 <C-booleanexpr> 傳回 0,則會引發例外。

注意

如果未使用 check(..),則 F2PY 會自動產生一些標準檢查(例如,在陣列引數的情況下,它會檢查正確的形狀和大小)。使用 check() 停用 F2PY 產生的檢查。

depend([<names>])

這宣告對應的引數取決於 <names> 列表中變數的值。例如,<init_expr> 可能使用其他引數的值。透過使用 depend(..) 屬性給出的資訊,F2PY 確保引數以正確的順序初始化。如果未使用 depend(..) 屬性,則 F2PY 會自動確定依賴關係。使用 depend() 停用 F2PY 產生的依賴關係。

當您編輯最初由 F2PY 產生的依賴關係時,請小心不要破壞其他相關變數的依賴關係。另一個需要注意的事項是循環依賴。F2PY 能夠在建構包裝器時偵測循環依賴,並且如果發現任何循環依賴,它會發出警告。

allocatable

對應的變數是 Fortran 90 可分配陣列,定義為 Fortran 90 模組資料。

external

對應的引數是使用者提供的函數。此回呼函數的簽名可以定義為

  • __user__ 模組區塊中,

  • 或透過示範性呼叫(或真實呼叫,如果簽名檔是真實的 Fortran 程式碼)在 <other statements> 區塊中。

例如,F2PY 從以下內容產生

external cb_sub, cb_fun
integer n
real a(n),r
call cb_sub(a,n)
r = cb_fun(4)

以下回呼簽名

subroutine cb_sub(a,n)
    real dimension(n) :: a
    integer optional,check(len(a)>=n),depend(a) :: n=len(a)
end subroutine cb_sub
function cb_fun(e_4_e) result (r)
    integer :: e_4_e
    real :: r
end function cb_fun

然後對應的使用者提供的 Python 函數是

def cb_sub(a,[n]):
    ...
    return
def cb_fun(e_4_e):
    ...
    return r

另請參閱 intent(callback) 屬性。

parameter

這表示對應的變數是一個參數,並且必須具有固定值。F2PY 將所有參數出現的位置替換為其對應的值。

擴展#

F2PY 指令#

F2PY 指令允許在 Fortran 77/90 原始碼中使用 F2PY 簽名檔結構。透過此功能,可以(幾乎)完全跳過中間簽名檔的產生,並將 F2PY 直接應用於 Fortran 原始碼。

F2PY 指令具有以下形式

<comment char>f2py ...

其中固定格式和自由格式 Fortran 程式碼的允許註解字元分別為 cC*!#!。編譯器會忽略 <註解字元>f2py 後面的所有內容,但 F2PY 會將其讀取為正常的非註解 Fortran 行

注意

當 F2PY 找到帶有 F2PY 指令的行時,指令首先會被 5 個空格取代,然後重新讀取該行。

對於固定格式的 Fortran 程式碼,<註解字元> 當然必須位於檔案的第一列。對於自由格式的 Fortran 程式碼,F2PY 指令可以出現在檔案中的任何位置。

C 運算式#

C 運算式用於簽名檔的以下部分

  • <init_expr> 用於變數初始化;

  • check 屬性的 <C-booleanexpr>

  • dimension 屬性的 <arrayspec>

  • callstatement 陳述式,這裡也可以使用 C 多行區塊。

C 運算式可能包含

  • 標準 C 結構;

  • 來自 math.hPython.h 的函數;

  • 引數列表中的變數,大概根據給定的依賴關係在之前初始化;

  • 以下 CPP 巨集

    f2py_rank(<name>)

    傳回陣列 <name> 的秩。

    f2py_shape(<name>, <n>)

    傳回陣列 <name> 的第 <n> 個維度。

    f2py_len(<name>)

    傳回陣列 <name> 的長度。

    f2py_size(<name>)

    傳回陣列 <name> 的大小。

    f2py_itemsize(<name>)

    傳回陣列 <name> 的項目大小。

    f2py_slen(<name>)

    傳回字串 <name> 的長度。

為了初始化陣列 <陣列名稱>,F2PY 產生一個遍歷所有索引和維度的迴圈,該迴圈執行以下虛擬陳述式

<array name>(_i[0],_i[1],...) = <init_expr>;

其中 _i[<i>] 指的是第 <i> 個索引值,其範圍從 0shape(<陣列名稱>,<i>)-1

例如,從以下簽名產生的函數 myrange(n)

subroutine myrange(a,n)
  fortranname        ! myrange is a dummy wrapper
  integer intent(in) :: n
  real*8 intent(c,out),dimension(n),depend(n) :: a = _i[0]
end subroutine myrange

等效於 numpy.arange(n,dtype=float)

警告

當掃描 Fortran 程式碼時,F2PY 也可能會降低 C 運算式中的大小寫(請參閱 --[no]-lower 選項)。

多行區塊#

多行區塊以 '''(三個單引號)開頭,並以一些嚴格後續行中的 ''' 結尾。多行區塊只能在 .pyf 檔案中使用。多行區塊的內容可以是任意的(除了它不能包含 '''),並且不會對其應用任何轉換(例如,降低大小寫)。

目前,多行區塊可以用於以下結構中

  • 作為 callstatement 陳述式的 C 運算式;

  • 作為 callprotoargument 陳述式的 C 類型規範;

  • 作為 usercode 陳述式的 C 程式碼區塊;

  • 作為 pymethoddef 陳述式的 C 陣列列表;

  • 作為文件字串。

擴展的字元選擇器#

F2PY 擴展了字元選擇器規範,可用於簽名檔或 F2PY 指令中,如下所示

<extended-charselector> := <charselector>
                        | (f2py_len= <len>)

請參閱 字串 以了解用法。