跳轉到

指令碼開發 / 基本概念

DataFlux Func 中,存在一些 DataFlux Func 中特有的概念,本文件將對此進行說明。

1. 指令碼集、指令碼和函式

指令碼集、指令碼和函式可在「開發 / 指令碼庫」中建立,是 DataFlux Func 的核心概念,ID 在使用者建立 / 編寫程式碼時直接指定。

  • 「指令碼集」為數個指令碼的集合,ID 在建立時由使用者直接指定,且只能包含指令碼。
  • 「指令碼」即 Python 指令碼本身,必然屬於某一個指令碼集,ID 在建立時由使用者直接指定。
  • 「函式」在 DataFlux Func 中特指被 @DFF.API(...) 裝飾器裝飾的最頂層函式,可以被函式 API、定時任務等作為呼叫的入口函式。

指令碼集不是資料夾

指令碼集與資料夾類似,但這個「資料夾」與一般 Python 編碼中的資料夾無關

在 DataFlux Func 中進行編碼時,會大量涉及到指令碼集、指令碼和函式的 ID,並且這些 ID 之間存在緊密的聯絡。

指令碼集、指令碼和函式 ID 之間關係

按照指令碼集、指令碼和函式層級關係,下層概念的 ID 一定包含了上層概念的 ID。

假設存在一個 ID 為 demo 的指令碼集,那麼屬於此指令碼集的所有指令碼必然以 demo__(雙下劃線)開頭。

再假設此指令碼集下有 ID 為 demo__test的指令碼,它包含一個函式 def hello(...),那麼這個函式的 ID 即為 demo__test.hello

ID 示例表如下:

概念 ID 示例
指令碼集 demo
指令碼 demo__test
函式 demo__test.hello

編碼中相互引用

在 DataFlux Func 的指令碼中,允許引用另一指令碼來實現程式碼複用。

假設存在指令碼 demo__script_a,其包含一個函式 func_a()。那麼,在指令碼 demo__script_b 中引用此函式時,可以使用如下方法:

demo__script_a
1
2
def func_a():
    pass
demo__script_b
1
2
3
4
import demo__script_b

def test():
    return demo__script_b.func_a()

Python 的as語句也同樣可以使用:

demo__script_b
1
2
3
4
import demo__script_b as b

def test():
    return b.func_a()

也可以使用 from ... import 語句只匯入所需函式:

demo__script_b
1
2
3
4
from demo__script_b import func_a

def test():
    return func_a()

對於屬於同一個指令碼集的指令碼之間引用,可以忽略指令碼集 ID,以 __(雙下劃線)開頭的縮略形式表示:

demo__script_b
1
2
3
4
from __script_b import func_a

def test():
    return func_a()

儘可能使用縮略形式

在指令碼集內部相互引用應當儘量使用縮略形式(即忽略指令碼集 ID 並以 __ 開頭的形式)。

這樣,在將整個指令碼集克隆,指令碼集 ID 發生改變後,克隆出來的新指令碼集內的程式碼依然可以正確引用本指令碼集內的指令碼。

2. 聯結器

聯結器可在「開發 / 聯結器」中建立,是由 DataFlux Func 提供的連線外部系統的工具,ID 在建立時由使用者直接指定。

實際上,在 DataFlux Func 編寫 Python 程式碼與原版 Python 並無太大區別。開發者完全可以忽略聯結器,自行在程式碼中連線外部系統。

但對於一些存在連線池概念的外部系統來說,聯結器內建了連線池,可在函式反覆執行的過程中保持連結,避免反覆建立 / 關閉與外部系統的連線。

假設使用者已經配置好了一個 ID 為 mysql 的聯結器,那麼獲取這個聯結器的操作物件程式碼如下:

Python
1
mysql = DFF.CONN('mysql')

具體不同的聯結器具有不同的操作方法和引數,詳情請參考 指令碼開發 / 聯結器物件 DFF.CONN

3. 環境變數

環境變數可在「開發 / 環境變數」中建立,是由 DataFlux Func 提供的簡單 Key-Value 配置讀取工具,ID 在建立時由使用者直接指定。

環境變數特別適合用於同一套程式碼執行在不同環境下的場景。

如指令碼需要訪問的系統區分了測試 / 生產環境,那麼可以透過設定環境變數,實現在不改變程式碼的情況下,切換測試 / 生產環境。

假設使用者已經配置好了一個 ID 為 api_endpoint 的環境變數,那麼獲取這個環境變數值的程式碼如下:

Python
1
api_endpoint = DFF.ENV('api_endpoint')

4. 函式 API

函式 API 可在「管理 / 函式 API」中建立,是外部呼叫 DataFlux Func 中函式的一種常見方式,呼叫過程可選同步或非同步,同步執行時,函式執行完成後可以將結果直接返回給呼叫方。

為函式建立函式 API 後,支援多種不同的呼叫方法。

函式 API 支援 GETPOST 兩種方式。兩種不同方式的引數傳遞同時支援「簡化形式」、「標準形式」。

此外,POST 方式的「簡化」形式還支援檔案上傳,以下是各種呼叫方式的功能支援列表:

呼叫方式 傳遞 kwargs 引數 kwargs 引數型別 傳遞 options 檔案上傳 提交任意格式的 Body
GET 簡化形式 支援 僅限字串 不支援 不支援 不支援
GET 標準形式 支援 JSON 中的資料型別 支援 不支援 不支援
POST 簡化形式 支援 僅限字串 不支援 支援 支援
POST 標準形式 支援 JSON 中的資料型別 支援 不支援 不支援

傳遞方式不同會導致引數型別存在限制

對於 kwargs 中引數只能傳遞字串的呼叫方式,需要在函式中對引數進行型別轉換。在函式 API 列表,可以點選「API 呼叫示例」來檢視具體呼叫方式

假設存在如下函式:

Python
1
2
3
@DFF.API('我的函式')
def my_func(x, y):
    pass

假設為此函式建立的「函式 API」ID 為 func-api-xxxxx,傳遞的引數為 x=100(整數), y="hello"(字串)。

那麼,各種不同的呼叫方式如下:

GET 簡化形式傳參

如果函式的引數比較簡單,可以使用 GET 簡化形式傳遞引數,介面將更加直觀。

由於 URL 中傳遞引數時,無法區分字串的 "100" 和整數的 100, 所以函式在被呼叫時,接收到的引數都是字串。 函式需要自行對引數進行型別轉換。

Text Only
1
GET /api/v1/al/func-api-xxxxx/simplified?x=100&y=hello

為了便於閱讀,示例為 URLEncode 之前的內容,實際 URL 引數需要進行 URLEncode

GET 標準形式傳參

在某些情況下,如果無法傳送 POST 請求,也可以使用 GET 方式呼叫介面。

GET 標準形式傳參時,將整個 kwargs 進行 JSON 序列化後,作為 URL 引數傳遞即可。 由於引數實際還是以 JSON 格式傳送,因此引數的原始型別都會保留。 函式無需再對引數進行型別轉換。

如本例中,函式接收到的 x 引數即為整數,無需型別轉換。

Text Only
1
GET /api/v1/al/func-api-xxxxx?kwargs={"x":100,"y":"hello"}

為了便於閱讀,示例為 URLEncode 之前的內容,實際 URL 引數需要進行 URLEncode

POST 簡化形式傳參

在某些情況下,如果無法傳送請求體為 JSON 的 HTTP 請求, 那麼也可以類似 Form 表單的形式傳遞引數,各欄位名即為引數名。

由於 Form 表單提交資料時,無法區分字串的 "100" 和整數的 100,所以函式在被呼叫時,接收到的引數都是字串,函式需要自行對引數進行型別轉換。

Text Only
1
2
3
4
POST /api/v1/al/func-api-xxxxx/simplified
Content-Type: x-www-form-urlencoded

x=100&y=hello

此外,POST 簡化形式傳參還額外支援檔案上傳(引數/欄位名必須為 files), 需要使用 form-data/multipart 方式進行處理。

頁面 HTML 程式碼示例如下:

HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<html>
    <body>
        <h1>檔案上傳</h1>
        <input id="file" type="file" name="files" required />
        <input id="submit" type="submit" value="上傳"/>
    </body>
    <script>
        // 函式 API 地址(如本頁面與 DataFlux Func 不在同一個域名下,需要寫全 http://domain:prot/api/v1/al/func-api-xxxxx/simplified
        // 注意:上傳檔案必須使用簡化形式函式 API
        var API_URL = '/api/v1/al/func-api-xxxxx/simplified';

        document.querySelector('#submit').addEventListener('click', function(event) {
            // 點選上傳按鈕後,生成 FormData 物件後作為請求體傳送請求
            var data = new FormData();
            data.append('x', '100');
            data.append('y', 'hello');
            data.append('files', document.querySelector('#file').files[0]);

            var xhr = new XMLHttpRequest();
            xhr.open('POST', API_URL);
            xhr.send(data);
        });
    </script>
</html>

POST 標準形式傳參

POST 標準形式傳參是最常見的呼叫方式。 由於引數以 JSON 格式透過請求體傳送,因此引數的原始型別都會保留。 函式無需再對引數進行型別轉換。

如本例中,函式接收到的 x 引數即為整數,無需型別轉換。

Text Only
1
2
3
4
5
6
7
8
9
POST /api/v1/al/func-api-xxxxx
Content-Type: application/json

{
    "kwargs": {
        "x": 100,
        "y": "hello"
    }
}

5. 定時任務

定時任務可在「管理 / 定時任務」中建立,用於讓 DataFlux Func 定期自動呼叫函式。

為函式建立定時任務後,函式則會所指定的 Crontab 表示式定時執行,不需要外部進行呼叫。

正因為如此,所執行的函式的所有引數必須都已滿足,即:

  1. 函式不需要輸入引數
  2. 函式需要輸入引數,但都是可選引數
  3. 函式需要必選引數,並在定時任務中為其配置具體的值

區分函式執行時所屬執行功能

如果函式同時配置了「定時任務」和其他執行功能,並且希望在不同的執行功能中進行區分處理,可以判斷內建變數 _DFF_CRONTAB 區分:

Python
1
2
3
4
5
6
7
8
9
@DFF.API('我的函式')
def my_func(x, y):
    result = x + y

    if _DFF_CRON_EXPR:
        # 只在定時任務時輸出日誌
        print(f'x + y = {result}')

    return