python

Python __slots__ 介紹與教學

__slots__ 是 Python 類別必須認識的屬性,這個屬性為我們帶來節省記憶體資源以及增加存取(access)類別屬性(attribute)效率的好處,但相對地,它也犧牲原本方便擴充類別屬性的易用性。

總的來說,它是寫出高效率 Python 程式碼的一環,平常用不到它沒有關係,但如果要榨出更多記憶體資源以及效能的話, __slots__ 是一定能派上用場。

本文將透過各種範例認識 __slots__ 並學會如何運用它。

Posted on  Oct 16, 2023  in  Python 程式設計 - 中階  by  Amo Chen  ‐ 4 min read

Python - super() 函式與 MRO 詳解

Python 的物件導向程式設計(OOP)有 2 個一定要懂的東西:

  1. super() 函式
  2. MRO(Method Resolution Order) / 方法解析順序

如果不懂得這 2 個東西,就無法徹底解放類別(class)的力量,甚至可能導致寫出不夠彈性而且冗長的程式碼。

super() + MRO = 超級瑪利歐?(誤

本文將從 super() 函式開始講解,說明 Python 的 MRO(Method Resolution Order) ,並介紹 MRO 的特性在實務上的應用。

如果你無法正確回答以下範例結果的執行結果,那麽推薦你看完本文:

class Parent(object):
    NAME = 'Parent'
    def __str__(self):
        return self.NAME

class Child(Parent):
    NAME = 'Child'
    def __str__(self):
        return super().__str__()

c = Child()
print(c)

正確答案為: Child

Posted on  Oct 6, 2023  in  Python 程式設計 - 中階  by  Amo Chen  ‐ 6 min read

Python 的 typing.Protocol 怎麼使用?

Python 3.8 之後 typing 模組 新增 1 個 typing.Protocol 的 class 可以使用,這個 class 很適合用來給一些有實作特定方法的 class 們做 type annotation 。

舉個常見的交通工具作為例子,假設我們有 1 個函數接受任何有實作 move() 方法的 instance:

def move(x):
    x.move()

這時候可以用 typing.Protocol 將參數 x 加上 1 個 type hint, 讓彼此知道此處不管型別,只管是否有實作 move() 方法:

from typing import Protocol

class Movable(Protocol):
    def move(self):
        ...

def move(x: Movable):
    x.move()

加上 typing.Protocol 是否看起來清晰很多?

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

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