Python 程式設計 - 高階

Python contextvars 模組教學

大家都知道執行緒(Threads)之間會共用 Process 的記憶體,這種共用的情況有可能造成 Race Condition, 使得程式出現不可預期的行為或錯誤,所幸這個問題可以透過 threadling.local 解決。

而 Python 3.4 之後推出 asyncio 模組,使得 Python 具備執行非同步 I/O (asynchronous I/O) 的能力,開發者可以同時結合 multiprocessing , threading 以及 asyncio 將 Python 效能提升至全新檔次,但是當結合 threading 以及 asyncio 時,可能會遭遇一個問題, 多個協程(coroutines)可能會在同 1 個執行緒中執行,因此多個 coroutines 也可能有互相影響的情況!所以有了 contextvars 模組,用以解決 coroutines 互相干擾的情況,將每個 coroutine 以 Context 切開,避免互相干擾!

Thread-local variables are insufficient for asynchronous tasks that execute concurrently in the same OS thread. Any context manager that saves and restores a context value using threading.local() will have its context values bleed to other code unexpectedly when used in async/await code.

Posted on  Sep 21, 2023  in  Python 程式設計 - 高階  by  Amo Chen  ‐ 4 min read

Python 套件推薦 - rustimport 給你的 Python 一對翅膀

不可否認 Python 以其優異的生態系與社群資源為開發帶來速度優勢。

不過每個應用都有效率極限, Python 當然也不例外,而且 Python 天生比起 JavaScript, Java 等語言更容易遇到效能瓶頸,這時候通常有 3 種選擇:

  1. 改用效率較好的寫法或套件
  2. 部分功能/元件改用別的語言實作
  3. 全部用別的語言實作

選項 1 是最簡單的,只要能夠找到更快的寫法或者更好的套件,就能夠緩解問題,再撐一陣子;選項 3 則是最困難的,要有足夠的時間以及負責人的支持之下,才有可能進行,畢竟所有的功能都必須改寫之外,測試也是需要全部重新來過,更何況通常公司專案都有時程壓力,選項 3 通常都是難以說服高層的選項;至於選項 2 則是折衷選項,建議選項 1 已經無法解決問題時採用,目前很多 Python 套件為了效率也都會採用選項 2 的做法,例如 orjson 就是以 Rust 語言實作的 JSON parser 。

目前 Python 與 Rust 的介接主要靠 PyO3 , 不過步驟稍微複雜一些,本文將介紹如何透過 rustimport 套件,極度簡化介接 Python 與 Rust 的方法,讓開發 Python 擴充套件可以更快樂、簡便!

Posted on  Sep 18, 2023  in  Python 模組/套件推薦 , Python 程式設計 - 高階  by  Amo Chen  ‐ 4 min read

Python threading.local() 解說

我們都知道多個執行緒(thread)之間會共用 Process 的記憶體,那你覺得以下範例程式的執行結果會是什麼呢?這是 2 個執行緒分別做 +1 與 -1 運算各 100,000 次的 Python 程式:

import threading


def count(thread_name, step=1):
    global v
    for i in range(0, 100000):
        v += 1 * step
    print(f'{thread_name} -> ', v, flush=True)


v = 0
t1 = threading.Thread(target=count, args=('t1', 1, ))
t2 = threading.Thread(target=count, args=('t2', -1, ))
t1.start()
t2.start()
t1.join()
t2.join()

這段範例程式的執行結果,就跟本文要解說的 threading.local() 有關。

Posted on  Sep 4, 2023  in  Python 程式設計 - 高階  by  Amo Chen  ‐ 3 min read

零經驗也可的 PySpark 教學 - 資料輸出 (DataFrame writer)

通常操作 dataframe 完之後,都會需要將結果輸出到資料庫/檔案甚至是雲端服務。 PySpark 已經將相關的輸出都整合到 pyspark.sql.DataFrameWriter 類別,只要理解該類別,基本上就能夠輕鬆將 DataFrame 輸出。

本文將介紹 pyspark.sql.DataFrameWriter 以及幾個使用上值得注意的點。

Posted on  Feb 6, 2023  in  Python 模組/套件推薦 , Python 程式設計 - 高階  by  Amo Chen  ‐ 4 min read

優化 Python Docker Image Size - 從 multi-stage builds 到 distroless

Docker multi-stage builds 教學 一文介紹以 Golang 作為範例,示範如何用 Docker multi-stage builds 的功能,優化編譯 Docker image 的過程,以減少 Docker Image 的 size 。

Multi-stage builds 並不局限於 Golang 這類的編譯(compiled)語言才能使用,腳本(script)語言也能夠運用類似的技巧降低 Docker image size, 例如 Javascript, Python 等開發生態系也都能夠使用。

只是腳本語言需透過直譯器(interpreter)執行的天性,因此其 Docker image 終究難以像 Golang 這類編譯語言所產生的 image 來得小,但這並不代表 Python, Javascript 這類的 Docker image 並不值得使用 multi-stage builds, 優化 Docker image size 仍可以為部署(deployment)速度帶來優勢,同時也能減少網路傳輸所需付出的費用成本。

本文的 multi-stage builds 以 Python 範例出發,一路介紹到如何使用 Google 所提供 distroless 進一步優化 Docker image size 與安全性。

Posted on  Jan 6, 2023  in  Docker , Python 程式設計 - 高階  by  Amo Chen  ‐ 5 min read

零經驗也可的 PySpark 教學 - UDF (User Defined Function)

Spark SQL 提供許多好用的函式(functions),例如 concat() , count() , date_format() 等等,但這些內建函式不一定能夠滿足一切的需求,所以有時候需要做一些擴充以達到目的,此種讓使用者能夠進行擴充的功能就被稱為 UDF (User Defined Function), RDBMS 諸如 MySQL 與 PostgreSQL 等也都支援 UDF, 詳見:

  1. MySQL - Adding a Loadable Function
  2. PostgreSQL - User-Defined Functions

Spark 也同樣支援 UDF, 讓使用者能夠擴充函式。

Posted on  Dec 28, 2022  in  Python 模組/套件推薦 , Python 程式設計 - 高階  by  Amo Chen  ‐ 4 min read