二十五、爬取毛豆新車的數據
1004
2025-03-31
目錄
在瀏覽器中運行 Python:好處
實現同構 Web 開發
訪問 Web API
原型設計和 JavaScript 庫
向學生教授 Python
考慮性能
Having Fun
安裝 Brython
CDN安裝
GitHub 安裝
PyPI 安裝
安裝
Brython 安裝選項回顧
了解 Brython 的工作原理
Brython 核心組件
Brython 標準庫
Brython in Action
Brython 的內部結構
在瀏覽器中使用 Brython
Brython 中的 DOM API
在 Brython 中導入
Reduce Import Size
與 JavaScript 交互
JavaScript
瀏覽器網頁 API
網頁界面框架
WebAssembly
在 Brython 中應用異步開發
Brython 中的 JavaScript Promise
Ajax in Brython
Brython 中的異步 IO
分發和打包 Brython 項目
手動和自動 Web 部署
部署到 PyPI
部署到 CDN
創建 Google Chrome 擴展
JS 中的 Hello World 擴展
Python 中的 Hello World 擴展
測試和調試 Brython
Python 單元測試
Selenium
JavaScript 單元測試
在 Brython 中調試
探索 Brython 的替代品
Skulpt
轉密
Pyodide
pypy.js
結論
與 JavaScript 交互
Brython 允許 Python 代碼與 JavaScript 代碼交互。最常見的模式是從 Brython 訪問 JavaScript。反過來,雖然可能,但并不常見。您將在JavaScript 單元測試部分看到 JavaScript 調用 Python 函數的示例。
JavaScript
到目前為止,您已經體驗了一些 Python 代碼與 JavaScript 代碼交互的場景。特別是,您已經能夠通過調用 來顯示消息框browser.alert()。
您可以alert在 Brython 控制臺中運行的以下三個示例中看到實際操作,而不是在標準的 CPython 解釋器 shell 中:
>>>
>>> import browser >>> browser.alert("Real Python")
或者你可以使用window:
>>>
>>> from browser import window >>> window.alert("Real Python")
或者你可以使用this:
>>>
>>> from javascript import this >>> this().alert("Real Python")
由于Brython暴露出的新層,二者的全球性質alert()和window,你可以調用alert上browser.window,甚至上javascript.this。
以下是允許訪問 JavaScript 函數的主要 Brython 模塊:
除了瀏覽器中可用的 JavaScript 函數和 API,您還可以訪問您編寫的 JavaScript 函數。以下示例演示了如何從 Brython 訪問自定義 JavaScript 函數:
1 2 3
4 5 19 20這是它的工作原理:
第 9 行定義myMessageBox()了 JavaScript 塊中的自定義函數。
第 17 行調用myMessageBox().
您可以使用相同的功能訪問 JavaScript 庫。您將在Web UI 框架部分了解如何與 Vue.js(一種流行的 Web UI 框架)進行交互。
瀏覽器網頁 API
瀏覽器公開了您可以從 JavaScript 訪問的 Web API,而 Brython 可以訪問相同的 API。在本節中,您將擴展 Base64 計算器以在瀏覽器頁面重新加載之間存儲數據。
允許此功能的Web API是Web Storage API。它包括兩種機制:
sessionStorage
localStorage
您將localStorage在接下來的示例中使用。
如前所述,Base64 計算器會創建一個字典,其中包含映射到此字符串的 Base64 編碼值的輸入字符串。加載頁面后數據會保留在內存中,但會在您重新加載頁面時清除。保存數據localStorage將在頁面重新加載之間保留字典。這localStorage是一個鍵值存儲。
要訪問localStorage,您需要導入storage.?為了貼近初步實現,你會加載和字典數據保存到localStorage的JSON格式。保存和獲取數據的關鍵是b64data.?修改后的代碼包括新的導入和一個load_data()函數:
from browser.local_storage import storage import json, base64 def load_data(): data = storage.get("b64data") if data: return json.loads(data) else: storage["b64data"] = json.dumps({}) return {}
load_data()在加載 Python 代碼時執行。它從localStoragePython 字典中獲取 JSON 數據并填充該字典,該字典將用于在頁面生命周期內將數據保存在內存中。如果沒有找到b64data的localStorage,然后它創建一個空的字典鍵b64data中localStorage,并返回一個空的字典。
您可以load_data()通過展開下面的框來查看完整的 Python 代碼。它展示了如何使用localStorageWeb API 作為持久存儲,而不是依賴于臨時內存存儲,就像本示例的前一個實現一樣。
可訪問 localStorage 的完整源代碼顯示隱藏
您可以從browser和其他子模塊訪問所有 Web API 函數。Brython 文檔中提供了有關訪問 Web API 的高級文檔。有關更多詳細信息,您可以查閱Web API 文檔并使用Brython 控制臺來試驗 Web API。
在某些情況下,您可能需要在熟悉的 Python 函數和來自 Web API 的函數之間進行選擇。例如,在上面的代碼中,您使用 Python Base64 編碼base64.b64encode(),但您可以使用 JavaScript 的btoa():
>>>
>>> from browser import window >>> window.btoa("Real Python") 'UmVhbCBQeXRob24='
您可以在在線控制臺中測試這兩種變體。Usingwindow.btoa()僅適用于 Brython 上下文,而base64.b64encode()可以使用常規 Python 實現(如CPython )執行。請注意,在 CPython 版本中,base64.b64encode()將 abytearray作為參數類型,而 JavaScriptwindow.btoa()則采用字符串。
如果性能是一個問題,那么請考慮使用 JavaScript 版本。
網頁界面框架
Angular、React、Vue.js或Svelte等流行的 JavaScript UI 框架已成為前端開發人員工具包的重要組成部分,Brython 與其中一些框架無縫集成。在本節中,您將使用 Vue.js 版本 3 和 Brython 構建一個應用程序。
您將構建的應用程序是一個計算字符串散列的表單。這是正在運行的 HTML 頁面的屏幕截圖:
在bodyHTML頁面中定義的綁定和模板聲明:
如果您不熟悉 Vue,那么您將在下面快速介紹一些內容,但請隨時查閱官方文檔以獲取更多信息:
Vue.js 指令是特殊的屬性值,以 為前綴v-,提供 DOM 和Vue.js 組件值之間的動態行為和數據映射:
v-model.trim="input_text"將輸入值綁定到Vue 模型?input_text并修剪該值。
v-model="algo"將下拉列表的值綁定到algo.
v-for="name in algos"將選項值綁定到name.
Vue 模板用雙花括號括起來的變量表示。Vue.js 將相應的占位符替換為 Vue 組件中的相應值:
hash_value
name
事件處理程序由 at 符號 (@)標識,如 in@click="compute_hash"。
對應的 Python 代碼描述了 Vue 和附加的業務邏輯:
1from browser import alert, window 2from javascript import this 3import hashlib 4 5hashes = { 6 "sha-1": hashlib.sha1, 7 "sha-256": hashlib.sha256, 8 "sha-512": hashlib.sha512, 9} 10 11Vue = window.Vue 12 13def compute_hash(evt): 14 value = this().input_text 15 if not value: 16 alert("You need to enter a value") 17 return 18 hash_object = hashes[this().algo]() 19 hash_object.update(value.encode()) 20 hex_value = hash_object.hexdigest() 21 this().hash_value = hex_value 22 23def created(): 24 for name in hashes: 25 this().algos.append(name) 26 this().algo = next(iter(hashes)) 27 28app = Vue.createApp( 29 { 30 "el": "#app", 31 "created": created, 32 "data": lambda _: {"hash_value": "", "algos": [], "algo": "", "input_text": ""}, 33 "methods": {"compute_hash": compute_hash}, 34 } 35) 36 37app.mount("#app")
Vue.js 的聲明性質顯示在帶有 Vue 指令和模板的 HTML 文件中。它還在 Python 代碼中通過第 11 行和第 28 至 35 行的 Vue 組件聲明進行了演示。這種聲明性技術將 DOM 的節點值與 Vue 數據連接起來,允許框架的反應性行為。
這消除了您必須在前一個示例中編寫的一些樣板代碼。例如,請注意,您不必從 DOM 中使用類似document["some_id"].?創建 Vue 應用程序并調用app.mount()處理 Vue 組件到相應 DOM 元素的映射以及 JavaScript 函數的綁定。
在 Python 中,訪問 Vue 對象字段需要您通過以下方式引用 Vue 對象javascript.this():
第 14 行獲取組件字段的值this().input_text。
第 21 行更新數據組件this().hash_value。
第 25 行向列表中添加了一個算法this().algos。
第 26 行this().algo使用 的第一個鍵進行實例化hashes{}。
如果 Vue 與 Brython 的結合引起了您的興趣,那么您可能想查看vuepy 項目,它為 Vue.js 提供完整的 Python 綁定并使用 Brython 在瀏覽器中運行 Python。
WebAssembly
在某些情況下,您可以使用WebAssembly來提高 Brython 甚至 JavaScript 的性能。WebAssembly或Wasm是所有主要瀏覽器都支持的二進制代碼。它可以在瀏覽器中提供優于 JavaScript 的性能改進,并且是C、C++和Rust等語言的編譯目標。如果您不使用 Rust 或 Wasm,則可以跳過本節。
在以下演示使用 WebAssembly 的方法的示例中,您將在 Rust 中實現一個函數并從 Python 調用它。
這不是一個徹底的 Rust 教程。它只會劃傷表面。有關 Rust 的更多詳細信息,請查看Rust 文檔。
通過啟動安裝銹使用rustup。要編譯 Wasm 文件,您還需要添加wasm32目標:
$ rustup target add wasm32-unknown-unknown
使用cargo在 Rust 安裝期間安裝的項目創建一個項目:
$ cargo new --lib op
上面的命令在名為op.?在此文件夾中,您將找到Cargo.tomlRust 構建配置文件,您需要修改該文件以表明您要創建動態庫。您可以通過添加突出顯示的部分來做到這一點:
[package] name = "op" version = "0.1.0" authors = ["John
src/lib.rs通過將其內容替換為以下內容進行修改:
#[no_mangle] pub extern fn double_first_and_add(x: u32, y: u32) -> u32 { (2 * x) + y }
在項目的根目錄中Cargo.toml,編譯你的項目:
$ cargo build --target wasm32-unknown-unknown
接下來,創建一個web包含以下內容的目錄index.html:
1 2 3 4
5 7 8 9 10 23 24上面的第 6 行main.py從同一目錄加載以下內容:
1from browser import document, window 2 3double_first_and_add = None 4 5def add_rust_fn(module): 6 global double_first_and_add 7 double_first_and_add = module.instance.exports.double_first_and_add 8 9def add_numbers(evt): 10 nb1 = document["number-1"].value or 0 11 nb2 = document["number-2"].value or 0 12 res = double_first_and_add(nb1, nb2) 13 document["result"].innerHTML = f"Result: ({nb1} * 2) + {nb2} = {res}" 14 15document["submit"].bind("click", add_numbers) 16window.WebAssembly.instantiateStreaming(window.fetch("op.wasm")).then(add_rust_fn)
突出顯示的行是允許 Brython 訪問 Rust 函數的粘合劑double_first_and_add():
第 16 行讀取op.wasmusing?WebAssembly,然后add_rust_fn()在下載 Wasm 文件時調用。
第 5 行實現了add_rust_fn(),它將 Wasm 模塊作為參數。
第 7 行分配double_first_and_add()給本地double_first_and_add名稱以使其可用于 Python。
在同一web目錄中,op.wasm從target/wasm32-unknown-unknown/debug/op.wasm以下位置復制:
$ cp target/wasm32-unknown-unknown/debug/op.wasm web
項目文件夾布局如下所示:
├── Cargo.lock ├── Cargo.toml ├── src │ └── lib.rs ├── target │ ... └── web ├── index.html ├── main.py └── op.wasm
這顯示了使用cargo new.?為清楚起見,target部分省略。
現在在web以下位置啟動服務器:
$ python3 -m http.server Serving HTTP on :: port 8000 (http://[::]:8000/) ...
最后,將您的 Internet 瀏覽器指向http://localhost:8000.?您的瀏覽器應呈現如下所示的頁面:
這個項目展示了如何創建一個可以從 JavaScript 或 Brython 使用的 WebAssembly。由于構建 Wasm 文件會產生大量開銷,因此這不應是您解決特定問題的首選方法。
如果 JavaScript 不能滿足您的性能要求,那么 Rust 可能是一個選擇。如果您已經擁有要與之交互的 Wasm 代碼(您構建的代碼或現有的 Wasm 庫),這將非常有用。
使用 Rust 生成 WebAssembly 的另一個可能的好處是它可以訪問 Python 或 JavaScript 中不存在的庫。如果您想使用用 C 語言編寫并且不能與 Brython 一起使用的 Python 庫,它也很有用。如果 Rust 中存在這樣的庫,那么您可能會考慮構建一個 Wasm 文件以將其與 Brython 一起使用。
在 Brython 中應用異步開發
同步編程是您可能最熟悉的計算行為。例如,當執行 A、B 和 C 三個語句時,程序首先執行 A,然后是 B,最后是 C。每個語句在將其傳遞到下一個之前都會阻塞程序的流程。
想象一種技術,A 將首先被執行,B 將被調用但不會立即執行,然后 C 將被執行。您可以將 B 視為將來被執行的承諾。因為 B 是非阻塞的,所以它被認為是異步的。有關異步編程的其他背景知識,您可以查看Python 中的異步功能入門。
JavaScript 是單線程的,特別是在涉及網絡通信時依賴于異步處理。例如,獲取 API 的結果不需要阻止其他 JavaScript 函數的執行。
使用 Brython,您可以通過許多組件訪問異步功能:
JavaScript 回調
JavaScript 承諾
browser.ajax
browser.aio
隨著 JavaScript 的發展,回調已逐漸被承諾或異步函數取代。在本教程中,您將學習如何使用來自 Brython 的 promise 以及如何使用browser.ajax和browser.aio模塊,它們利用了 JavaScript 的異步特性。
CPython 庫中的asyncio模塊不能在瀏覽器上下文中使用,在 Brython 中被替換為browser.aio.
Brython 中的 JavaScript Promise
在 JavaScript 中,promise是一個可能在未來某個時候產生結果的對象。完成后產生的值將是一個值或錯誤的原因。
下面的示例說明了如何使用Promise來自 Brython的 JavaScript對象。您可以在在線控制臺中使用此示例:
>>>
1>>> from browser import timer, window 2>>> def message_in_future(success, error): 3... timer.set_timeout(lambda: success("Message in the future"), 3000) 4... 5>>> def show_message(msg): 6... window.alert(msg) 7... 8>>> window.Promise.new(message_in_future).then(show_message) 9
在 Web 控制臺中,您可以立即獲得有關 Python 代碼執行的反饋:
第 1 行導入timer設置超時并window訪問Promise對象。
第 2 行定義了一個executor,message_in_future()當承諾成功時,在超時結束時返回一條消息。
第 5 行定義了一個show_message()顯示警報的函數。
第 8 行創建了一個帶有 executor 的 promise,用一個then塊鏈接,允許訪問 promise 的結果。
在上面的例子中,超時人為地模擬了一個長時間運行的函數。承諾的實際使用可能涉及網絡調用。經過3秒,許用值成功完成"Message in the future"。
如果執行程序函數message_in_future()檢測到錯誤,則它可以error()將錯誤原因作為參數進行調用。您可以使用新的鏈式方法.catch(), 在Promise對象上實現這一點,如下所示:
>>>
>>> window.Promise.new(message_in_future).then(show_message).catch(show_message)
您可以在下圖中看到成功完成承諾的行為:
這個項目展示了如何創建一個可以從 JavaScript 或 Brython 使用的 WebAssembly。由于構建 Wasm 文件會產生大量開銷,因此這不應是您解決特定問題的首選方法。
如果 JavaScript 不能滿足您的性能要求,那么 Rust 可能是一個選擇。如果您已經擁有要與之交互的 Wasm 代碼(您構建的代碼或現有的 Wasm 庫),這將非常有用。
使用 Rust 生成 WebAssembly 的另一個可能的好處是它可以訪問 Python 或 JavaScript 中不存在的庫。如果您想使用用 C 語言編寫并且不能與 Brython 一起使用的 Python 庫,它也很有用。如果 Rust 中存在這樣的庫,那么您可能會考慮構建一個 Wasm 文件以將其與 Brython 一起使用。
在 Brython 中應用異步開發
同步編程是您可能最熟悉的計算行為。例如,當執行 A、B 和 C 三個語句時,程序首先執行 A,然后是 B,最后是 C。每個語句在將其傳遞到下一個之前都會阻塞程序的流程。
想象一種技術,A 將首先被執行,B 將被調用但不會立即執行,然后 C 將被執行。您可以將 B 視為將來被執行的承諾。因為 B 是非阻塞的,所以它被認為是異步的。有關異步編程的其他背景知識,您可以查看Python 中的異步功能入門。
JavaScript 是單線程的,特別是在涉及網絡通信時依賴于異步處理。例如,獲取 API 的結果不需要阻止其他 JavaScript 函數的執行。
使用 Brython,您可以通過許多組件訪問異步功能:
JavaScript 回調
JavaScript 承諾
browser.ajax
browser.aio
隨著 JavaScript 的發展,回調已逐漸被承諾或異步函數取代。在本教程中,您將學習如何使用來自 Brython 的 promise 以及如何使用browser.ajax和browser.aio模塊,它們利用了 JavaScript 的異步特性。
CPython 庫中的asyncio模塊不能在瀏覽器上下文中使用,在 Brython 中被替換為browser.aio.
Brython 中的 JavaScript Promise
在 JavaScript 中,promise是一個可能在未來某個時候產生結果的對象。完成后產生的值將是一個值或錯誤的原因。
下面的示例說明了如何使用Promise來自 Brython的 JavaScript對象。您可以在在線控制臺中使用此示例:
>>>
1>>> from browser import timer, window 2>>> def message_in_future(success, error): 3... timer.set_timeout(lambda: success("Message in the future"), 3000) 4... 5>>> def show_message(msg): 6... window.alert(msg) 7... 8>>> window.Promise.new(message_in_future).then(show_message) 9
在 Web 控制臺中,您可以立即獲得有關 Python 代碼執行的反饋:
第 1 行導入timer設置超時并window訪問Promise對象。
第 2 行定義了一個executor,message_in_future()當承諾成功時,在超時結束時返回一條消息。
第 5 行定義了一個show_message()顯示警報的函數。
第 8 行創建了一個帶有 executor 的 promise,用一個then塊鏈接,允許訪問 promise 的結果。
在上面的例子中,超時人為地模擬了一個長時間運行的函數。承諾的實際使用可能涉及網絡調用。經過3秒,許用值成功完成"Message in the future"。
如果執行程序函數message_in_future()檢測到錯誤,則它可以error()將錯誤原因作為參數進行調用。您可以使用新的鏈式方法.catch(), 在Promise對象上實現這一點,如下所示:
>>>
>>> window.Promise.new(message_in_future).then(show_message).catch(show_message)
您可以在下圖中看到成功完成承諾的行為:
在控制臺中運行代碼時,可以看到Promise先創建了對象,然后在超時后顯示消息框。
Ajax in Brython
當函數被限定為I/O bound時,異步函數特別有用。這與受CPU 限制的函數形成對比。一個I / O綁定函數是大多花費時間等待輸入或輸出到結束,而函數CPU限制功能被計算。通過網絡調用 API 或查詢數據庫是受 I/O 限制的執行,而計算素數序列則受 CPU 限制。
Brython的browser.ajax自曝HTTP一樣的功能get()和post()是,默認情況下,異步的。這些函數采用blocking可以設置為True同步呈現相同函數的參數。
要異步調用HTTPGET,請ajax.get()按如下方式調用:
ajax.get(url, oncomplete=on_complete)
要以阻塞模式獲取 API,請將blocking參數設置為True:
ajax.get(url, blocking=True, oncomplete=on_complete)
以下代碼顯示了進行阻塞 Ajax 調用和非阻塞 Ajax 調用之間的區別:
1from browser import ajax, document 2import javascript 3 4def show_text(req): 5 if req.status == 200: 6 log(f"Text received: '{req.text}'") 7 else: 8 log(f"Error: {req.status} - {req.text}") 9 10def log(message): 11 document["log"].value += f"{message} \n" 12 13def ajax_get(evt): 14 log("Before async get") 15 ajax.get("/api.txt", oncomplete=show_text) 16 log("After async get") 17 18def ajax_get_blocking(evt): 19 log("Before blocking get") 20 try: 21 ajax.get("/api.txt", blocking=True, oncomplete=show_text) 22 except Exception as exc: 23 log(f"Error: {exc.__name__} - Did you start a local web server?") 24 else: 25 log("After blocking get") 26 27document["get-btn"].bind("click", ajax_get) 28document["get-blocking-btn"].bind("click", ajax_get_blocking)
上面的代碼說明了同步和異步兩種行為:
第 13 行定義了ajax_get(),它使用ajax.get().?的默認行為ajax.get()是異步的。ajax_get()返回,并show_text()分配給參數oncomplete在接收到遠程文件后回調/api.txt。
第 18 行定義了ajax_get_blocking(),它演示了如何使用ajax.get()阻塞行為。在這種情況下,show_text()在ajax_get_blocking()返回之前調用。
當您運行完整示例并單擊Async Get和Blocking Get 時,您將看到以下屏幕:
可以看到,在第一個場景中,ajax_get()是完全執行的,API 調用的結果是異步發生的。在第二種情況下,在從 返回之前顯示 API 調用的結果ajax_get_blocking()。
Brython 中的異步 IO
隨著asyncio,Python 3.4 開始公開新的異步功能。在 Python 3.5 中,異步支持通過async/await語法得到了豐富。由于與瀏覽器事件循環不兼容,Brython 實現browser.aio為標準的替代品asyncio。
Brython 模塊browser.aio和 Python 模塊asyncio都支持使用asyncandawait關鍵字并共享通用函數,如run()and?sleep()。這兩個模塊都實現了其他不同的函數,這些函數屬于它們各自的執行上下文、CPython 上下文環境asyncio和瀏覽器環境browser.aio。
您可以使用run()和sleep()來創建協程。為了說明在 Brython 中實現的協程的行為,您將實現CPython 文檔中提供的協程示例的變體:
1from browser import aio as asyncio 2import time 3 4async def say_after(delay, what): 5 await asyncio.sleep(delay) 6 print(what) 7 8async def main(): 9 print(f"started at {time.strftime('%X')}") 10 11 await say_after(1, 'hello') 12 await say_after(2, 'world') 13 14 print(f"finished at {time.strftime('%X')}") 15 16asyncio.run(main())
除了第一import行,代碼與您在 CPython 文檔中找到的相同。它演示了關鍵字asyncand的使用await和顯示run()和sleep()實際操作:
1層線的用途asyncio為的別名browser.aio。盡管它隱藏了aio,但它使代碼接近 Python 文檔示例以方便比較。
第 4 行聲明了協程say_after()。注意使用async.
第 5 行調用asyncio.sleep()withawait以便當前函數將控制權交給另一個函數直到sleep()完成。
第 8 行聲明了另一個協程,該協程本身將調用該協程say_after()兩次。
第 9 行調用run(),一個非阻塞函數,它接受一個協程——main()在這個例子中——作為參數。
請注意,在瀏覽器的上下文中,aio.run()利用了內部 JavaScript 事件循環。這與asyncio.run()CPython 中的相關函數不同,后者完全管理事件循環。
要執行此代碼,請將其粘貼到在線 Brython 編輯器中,然后單擊Run。您應該得到類似于以下屏幕截圖的輸出:
首先執行腳本,然后"hello"顯示,最后"world"顯示。
有關 Python 中協程的更多詳細信息,您可以查看Python 中的異步 IO:完整演練。
異步 I/O 的通用概念適用于所有采用這種模式的平臺。在 JavaScript 中,事件循環本質上是環境的一部分,而在 CPython 中,這是使用asyncio.
上面的示例是有意練習,以保持代碼與 Python 文檔示例中顯示的完全相同。使用 Brython 在瀏覽器中進行編碼時,建議顯式使用browser.aio,正如您將在下一節中看到的。
要向 API 發出異步調用,如上一節所述,您可以編寫如下函數:
async def process_get(url): req = await aio.get(url)
請注意關鍵字async和的使用await。該函數需要定義為async使用await.?在此函數的執行過程中,當到達對 的調用時await aio.get(url),該函數將控制權交還給主事件循環,同時等待網絡調用aio.get()完成。其余的程序執行不會被阻塞。
以下是如何調用的示例process_get():
aio.run(process_get("/some_api"))
該函數aio.run()執行協程process_get()。它是非阻塞的。
一個更完整的代碼示例展示了如何使用關鍵字asyncandawait以及如何aio.run()和aio.get()是互補的:
1from browser import aio, document 2import javascript 3 4def log(message): 5 document["log"].value += f"{message} \n" 6 7async def process_get(url): 8 log("Before await aio.get") 9 req = await aio.get(url) 10 log(f"Retrieved data: '{req.data}'") 11 12def aio_get(evt): 13 log("Before aio.run") 14 aio.run(process_get("/api.txt")) 15 log("After aio.run") 16 17document["get-btn"].bind("click", aio_get)
在 Python 3 的最新版本中,您可以使用async和await關鍵字:
第 7 行定義process_get()了關鍵字async。
第 9 行aio.get()使用關鍵字 進行調用await。Usingawait要求用 定義封閉函數async。
第 14 行顯示了如何使用aio.run(),它將async要調用的函數作為參數。
要運行完整示例,您需要啟動 Web 服務器。您可以使用python3 -m http.server.?它在端口 8000 和默認頁面上啟動本地 Web 服務器index.html:
屏幕截圖顯示了單擊Async Get后執行的步驟序列。使用aio模塊和關鍵字的組合,async并await展示了如何接受 JavaScript 推廣的異步編程模型。
分發和打包 Brython 項目
您用于安裝 Brython 的方法可能會影響您部署 Brython 項目的方式和位置。特別是,要部署到 PyPI,最好的選擇是首先從 PyPI 安裝 Brython,然后使用brython-cli.?但是到私有服務器或云提供商的典型 Web 部署可以利用您選擇的任何安裝方法。
您有幾個部署選項:
手動和自動部署
部署到 PyPI
部署到 CDN
您將在以下部分中探索其中的每一個。
手動和自動 Web 部署
您的應用程序包含網站所需的所有靜態依賴項、CSS、JavaScript、Python 和圖像文件。Brython 是 JavaScript 文件的一部分。所有文件都可以按原樣部署在您選擇的提供商上。您可以查閱Web 開發教程和使用 Fabric 和 Ansible 自動化 Django 部署,了解有關部署 Brython 應用程序的詳細信息。
如果你決定使用brython-cli --modules預編譯Python代碼,然后將文件部署不會有任何Python源代碼,只brython.js和brython_modules.js。您也不會包含,brython_stdlib.js因為所需的模塊brython_modules.js已經包含在其中。
部署到 PyPI
當你從 PyPI 安裝 Brython 時,你可以brython-cli用來創建一個可以部署到 PyPI 的包。創建這樣一個包的目標是擴展默認的 Brython 模板作為自定義項目的基礎,并使 Brython 網站可以從 PyPI 中使用。
按照從 PyPI 安裝部分中的說明進行操作后,在新web項目中執行以下命令:
$ brython-cli --make_dist
系統會提示您回答幾個旨在創建 的問題brython_setup.json,您可以稍后修改這些問題。完成命令后,您將擁有一個名為的目錄,__dist__其中包含創建可安裝包所需的文件。
您可以在本地測試這個新包的安裝,如下所示:
$ pip install -e __dist__
隨后,您還可以web通過執行以下命令來確認新命令是否與包一起部署在本地:
$ python -m web --help usage: web.py [-h] [--install] optional arguments: -h, --help show this help message and exit --install Install web in an empty directory
請注意,該web命令的行為與 Brython 在初始安裝后的行為完全相同。您剛剛創建了一個可部署到 PyPI 的自定義可安裝 Brython 包。有關如何將包部署到 PyPI 的詳細說明,請查看如何將開源 Python 包發布到 PyPI。
部署到 PyPI 后,您可以pip在Python 虛擬環境中安裝 Brython 包。您將能夠使用您創建的新命令創建新的自定義應用程序:
$ python -m
總而言之,以下是部署到 PyPI 的步驟:
從 PyPI 安裝 Brython。
使用brython-cli --install.
使用brython-cli --make-dist.
將此包部署到 PyPI。
其他安裝方法(CDN、GitHub 和 npm)不包括在內brython-cli,因此不太適合準備 PyPI 包。
部署到 CDN
就像CDN 服務器上可用的brython.js和brython_stdlibs.js一樣,您還可以將靜態資產、圖像、樣式和 JavaScript 文件(包括 Python 文件或 )部署brython_modules.js到 CDN。CDN 的示例包括:
Cloudflare
Google Cloud CDN
Azure CDN
Amazon CloudFront
Akamai
如果您的應用程序是開源的,那么您可以獲得免費的 CDN 支持。示例包括CDNJS和jsDelivr。
創建 Google Chrome 擴展
Chrome 擴展程序是使用網絡技術構建的組件,并集成到 Chrome 中以自定義您的瀏覽環境。通常,這些擴展程序的圖標顯示在 Chrome 窗口的頂部,地址欄的右側。
公共擴展程序可從Chrome 網上應用店獲得。要學習,您將從本地文件安裝 Google Chrome 擴展程序:
在 Brython 中實現 Google Chrome 擴展之前,您將首先實現一個 JavaScript 版本,然后將其轉換為 Brython。
JS 中的 Hello World 擴展
作為初學者,您將實現一個將執行以下操作的擴展:
單擊擴展程序圖標時打開一個彈出窗口
單擊彈出窗口按鈕時打開提示消息
在初始彈出窗口的底部附加您輸入的消息
以下屏幕截圖說明了此行為:
在一個空文件夾中,創建文件manifest.json來配置擴展:
1// manifest.json 2{ 3 "name": "JS Hello World", 4 "version": "1.0", 5 "description": "Hello World Chrome Extension in JavaScript", 6 "manifest_version": 2, 7 "browser_action": { 8 "default_popup": "popup.html" 9 }, 10 "permissions": ["declarativeContent", "storage", "activeTab"] 11}
此示例的重要字段是默認彈出文件 ,popup.html您還必須創建該文件。有關其他字段的信息以及更多信息,您可以查閱清單文件格式文檔。
在同一文件夾中,創建popup.html用于定義擴展用戶界面的文件:
1 2 3 4
5 6 7 8 9 10 11 12 13HTML 代碼與您用于在 JavaScript 中創建 Chrome 擴展程序的代碼非常相似。有幾個細節值得注意:
第 5 行brython.min.js從本地包加載。出于安全原因,僅加載本地腳本,您無法從 CDN 等外部源加載。
第 6 行加載init_brython.js,它調用brython().
7號線負載popup.py。
第 9 行聲明body沒有通常的onload="brython()".
另一個安全約束會阻止您brython()在onload發生body標記時調用。解決方法是在文檔中添加一個-brython(),并在文檔內容加載后指示瀏覽器執行:
// init_brython.js document.addEventListener('DOMContentLoaded', function () { brython(); });
最后,您可以在以下 Python 代碼中看到此應用程序的主要邏輯:
# popup.py from browser import document, prompt def hello(evt): default = "Real Python" name = prompt("Enter your name:", default) if not name: name = default document["hello"].innerHTML = f"Hello, {name}!" document["hello-btn"].bind("click", hello)
這樣,您就可以像處理 JavaScript chrome 擴展一樣繼續安裝和測試了。
測試和調試 Brython
目前沒有方便的庫來單元測試 Brython 代碼。隨著 Brython 的發展,您將看到更多用于在瀏覽器中測試和調試 Python 代碼的選項。可以將 Python 單元測試框架用于可在瀏覽器外部使用的獨立 Python 模塊。在瀏覽器中,帶有瀏覽器驅動程序的 Selenium 是一個不錯的選擇。調試也是有限的,但也是可能的。
Python 單元測試
Python 單元測試框架,如內置的unittest和pytest,在瀏覽器中不起作用。您可以將這些框架用于 Python 模塊,這些模塊也可以在 CPython 的上下文中執行。任何 Brython 特定的模塊browser都無法在命令行中使用此類工具進行測試。有關 Python 單元測試的更多信息,請查看 Python 測試入門。
Selenium
Selenium是一個自動化瀏覽器的框架。它與瀏覽器中使用的語言無關,無論是 JavaScript、Elm、Wasm 還是 Brython,因為它使用WebDriver概念來表現得像用戶與瀏覽器交互。您可以查看使用 Python 和 Selenium進行現代 Web 自動化以獲取有關此框架的更多信息。
JavaScript 單元測試
有許多專注于 JavaScript 的測試框架,如Mocha、Jasmine和QUnit,在整個 JavaScript 生態系統中表現良好。但它們不一定非常適合對瀏覽器中運行的 Python 代碼進行單元測試。一種選擇需要將 Brython 函數全局公開給 JavaScript,這與最佳實踐背道而馳。
為了說明將 Brython 函數公開給 JavaScript 的選項,您將使用QUnit,這是一個 JavaScript 單元測試套件,可以在 HTML 文件中獨立運行:
1 2 3 4 5
6 7 8在一個 HTML 文件中,您編寫了 Python 代碼、JavaScript 代碼和 JavaScript 測試來驗證在瀏覽器中執行的兩種語言的函數:
第 11 行導入 QUnit 框架。
第 23 行暴露python_add()給 JavaScript。
第 28 行定義js_add_test測試 JavaScript 函數js_add()。
第 34 行定義py_add_test了測試 Python 函數python_add()。
第 40 行定義py_add_failed_test了測試 Python 函數python_add()的錯誤。
您不需要啟動 Web 服務器來執行單元測試。index.html在瀏覽器中打開,您應該會看到以下內容:
該頁面顯示了兩個成功的測試,js_add_test()和py_add_test(),一個失敗的測試,py_add_failed_test()。
將 Python 函數暴露給 JavaScript 展示了如何使用 JavaScript 單元測試框架在瀏覽器中執行 Python。雖然可以進行測試,但一般不建議這樣做,因為它可能與現有的 JavaScript 名稱沖突。
在 Brython 中調試
在撰寫本文時,還沒有用戶友好的工具來調試您的 Brython 應用程序。您無法生成允許您在瀏覽器開發工具中逐步調試的源映射文件。
這不應該阻止您使用 Brython。以下是一些有助于調試和排除 Brython 代碼故障的提示:
使用print()或browser.console.log()在瀏覽器的開發人員工具控制臺中打印變量值。
使用Python 3.8中的酷新功能中所述的f-string 調試。
偶爾使用開發者工具清除瀏覽器的 IndexedDB。
通過選中瀏覽器開發人員工具的網絡選項卡中的禁用緩存復選框,在開發過程中禁用瀏覽器緩存。
添加選項以brython()啟用要在 JavaScript 控制臺中顯示的其他調試信息。
復制brython.js和brython_stdlib.min.js本地復制以加快開發過程中的重新加載。
在您import編寫 Python 代碼時啟動本地服務器。
對Chrome 擴展程序進行故障排除時,從擴展程序打開檢查器。
Python 的優點之一是REPL(讀取-評估-打印循環)。在線 Brython 控制臺提供了一個平臺來試驗、測試和調試一些代碼片段的行為。
探索 Brython 的替代品
Brython 并不是在瀏覽器中編寫 Python 代碼的唯一選擇。有一些替代方案可用:
Skulpt
Transcrypt
Pyodide
PyPy.js
每個實現都從不同的角度解決問題。Brython 試圖通過提供對與 JavaScript 相同的 Web API 和 DOM 操作的訪問來替代 JavaScript,但具有 Python 語法和習語的吸引力。與某些可能具有不同目標的替代方案相比,它被打包為一個小下載。
這些框架如何比較?
Skulpt
雕塑在瀏覽器Python 代碼編譯為 JavaScript。編譯發生在頁面加載后,而在 Brython 中,編譯發生在頁面加載期間。
雖然它沒有內置函數來操作 DOM,但 Skulpt 在其應用程序上非常接近 Brython。這包括教育用途和成熟的 Python 應用程序,如Anvil 所示。
Skulpt 是一個朝著 Python 3 發展的維護項目。 Brython 在與 CPython 3.9 的模塊上與瀏覽器中的執行兼容。
轉密
Transcrypt包含一個命令行工具,用于將 Python 代碼編譯為 JavaScript 代碼。編譯據說是提前(AOT)。然后可以將生成的代碼加載到瀏覽器中。Transcrypt 占用空間小,大約 100KB。它速度快并且支持 DOM 操作。
Skulpt 和 Brython 的區別在于,Transcrypt 在下載并在瀏覽器中使用之前,先使用 Transcrypt 編譯器編譯為 JavaScript。這實現了速度和小尺寸。但是,它阻止了 Transcrypt 像其他平臺一樣被用作教育平臺。
Pyodide
Pyodide是 CPython 解釋器的 WebAssembly 編譯。它在瀏覽器中解釋 Python 代碼。沒有 JavaScript 編譯階段。盡管 Pyodide 與 PyPy.js 一樣,需要您下載大量數據,但它加載了NumPy、Pandas、Matplotlib等科學庫。
您可以將 Pyodide 視為完全在瀏覽器中運行的Jupyter Notebook環境,而不是由后端服務器提供服務。您可以使用一個活生生的例子來試驗 Pyodide?。
pypy.js
PyPy.js 使用通過emscripten編譯為 JavaScript的PyPy?Python 解釋器,使其兼容在瀏覽器中運行。
除了項目目前處于休眠狀態之外,PyPy.js 是一個大包,大約 10 MB,對于典型的 Web 應用程序來說是望而卻步。通過打開PyPy.js 主頁,您仍然可以使用 PyPy.js 作為在瀏覽器中學習 Python 的平臺。
PyPy.js 使用 emscripten 編譯為 JavaScript。Pyodide 更進一步,特別是利用 emscripten 和 Wasm 將 Python C 擴展(如NumPy)編譯為 WebAssembly。
在撰寫本文時,PyPy.js 似乎沒有得到維護。對于與編譯過程相同的內容,請考慮 Pyodide。
結論
在本教程中,您深入了解了在瀏覽器中編寫 Python 代碼的幾個方面。這可能會讓您對嘗試使用 Python 進行前端開發產生一些興趣。
在本教程中,您學習了如何:
在本地環境中安裝和使用 Brython
在前端 Web 應用程序中用 Python 替換 JavaScript
操作DOM
與JavaScript交互
創建瀏覽器擴展
比較Brython 的替代品
除了訪問通常為 JavaScript 保留的功能之外,Brython 的最佳用途之一是作為學習和教學工具。現在,您可以訪問在瀏覽器中運行的 Python編輯器和控制臺,開始探索 Brython 的多種用途。
API JavaScript Python
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。