2024年5月22日 星期三

Python 如何動態轉發到模組中的同名函式

Python 如何動態轉發到模組中的同名函式

這是上一篇文章的延伸,檔案結構請參考這篇
CHG: Python 如何動態載入模組中的所有的檔案 (charlottehong.blogspot.com)

主要是想把實作分開到 impl 檔案中,這裡是自動導入的寫法一樣追加在該包的初始話文件中


utils\__init__.py

import importlib
from functools import wraps

# 動態轉發與檔案名稱相同的函式或類別
def forward(target):
    # 裝飾器應用於函式時
    if callable(target):
        @wraps(target)
        def wrapper(*args, **kwargs):
            # 先執行原始函數(但不使用其返回值)
            target(*args, **kwargs)
            func_name = target.__name__
            try:
                # 動態導入目標模組
                parent_module = importlib.import_module(f'.{func_name}', package=__package__)
                # 獲取同名的函數
                target_func = getattr(parent_module, func_name, None)
                if target_func is None:
                    raise AttributeError(f"'{parent_module.__name__}' module has no attribute '{func_name}'")
                # 執行轉發函數並返回其結果
                return target_func(*args, **kwargs)
            except ImportError as e:
                raise ImportError(f"Failed to import module for function '{func_name}': {e}")
            except AttributeError as e:
                raise AttributeError(f"Function '{func_name}' not found in the module '{parent_module.__name__}': {e}")
        return wrapper
    # 裝飾器應用於類別時
    elif isinstance(target, type):
        for attr_name, attr_value in target.__dict__.items():
            if callable(attr_value) and not attr_name.startswith('__'):
                decorated_method = forward(attr_value)
                setattr(target, attr_name, decorated_method)
        return target
    else:
        raise TypeError("The @forward decorator can only be applied to functions or classes")


然後就可以在 main 中這樣使用

import utils

@utils.forward
def test_func(name):
    pass

@utils.forward
class test_class:
    pass

if __name__ == "__main__":
    test_func("CHG")
    test_class().method()

如此一來就可以自動轉發到同名的函式或類別上了


其中我有保留了原本函式的執行,這是為了把參數前置處理檢查之類的工作可以一同寫在參數定義上,轉發後的函式只需要專注在業務邏輯上

  • 執行順序是先原函式然後再轉發的函式
  • 原函式的返回值會被丟棄,以轉發的函式的返回值為主


新增在 utils 中有兩個檔案

utils\test_func.py

def test_func(name):
    print(f"[name = {name}]")

utils\test_class.py

class test_class:
    def method(self):
        print("This is a method from test_class in utils.test_class")






沒有留言:

張貼留言