本文旨在提供一種使用 python 反射機制獲取類及其父類中所有聲明或類型化的參數(shù)的方法。通過結(jié)合 inspect.get_annotations 和類的 __mro__ 屬性,我們可以提取包括父類在內(nèi)的所有類型注解,并將其組織成一個易于使用的字典。本文提供詳細的代碼示例和解釋,幫助讀者理解和應用這一技術。
使用 inspect.get_annotations 和 __mro__ 獲取參數(shù)類型信息
Python 的 inspect 模塊提供了強大的反射功能,其中 inspect.get_annotations 函數(shù)可以獲取指定對象的類型注解。類的 __mro__ 屬性則包含了類的繼承關系,即方法解析順序(Method Resolution Order)。結(jié)合這兩個工具,我們可以遍歷類的繼承鏈,提取所有父類的類型注解。
以下是一個示例代碼:
import inspect from typing import Any, Optional, Dict class Parent: parent_param: str parent_default: Optional[str] = None @classmethod def find_params_meta(cls) -> Dict[str, Any]: params_meta = {} for c in reversed(cls.__mro__): if hasattr(c, '__annotations__'): for k, v in inspect.get_annotations(c).items(): if k not in params_meta: # 避免子類覆蓋父類同名參數(shù)的類型信息 params_meta[k] = {"types": [v]} if hasattr(cls, k) and getattr(cls, k) != inspect._empty: params_meta[k]["default"] = getattr(cls, k) return params_meta class Child(Parent): child_param: str _params_meta: Optional[Dict[str, Any]] = None def __init__(self, parent_param: str, child_param: str): self._params_meta = self.find_params_meta() self.child_param = child_param self.parent_param = parent_param def __getattr__(self, name): # 模擬未初始化的屬性訪問 raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'") # 實例化對象并打印結(jié)果 child_instance = Child("parent param", "child param") print(child_instance._params_meta) # 斷言 expected_result = { "parent_param": {"types": [str]}, "parent_default": {"types": [str, type(None)], "default": None}, "child_param": {"types": [str]}, } assert child_instance._params_meta == expected_result
代碼解釋:
- Parent 類和 Child 類: 定義了父類 Parent 和子類 Child,包含類型注解的類屬性。
- find_params_meta 方法: 這是一個類方法,用于獲取類及其父類的類型注解。
- cls.__mro__: __mro__ 屬性返回一個包含類及其所有父類的元組,按照方法解析順序排列。 使用 reversed 反轉(zhuǎn)這個順序,確保子類的類型信息優(yōu)先。
- inspect.get_annotations(c): 獲取每個類 c 的類型注解,返回一個字典。
- 類型信息合并: 遍歷父類鏈時,將父類的類型信息合并到 params_meta 中。如果子類定義了與父類同名的參數(shù),則子類的類型信息優(yōu)先。
- 默認值獲取: 檢查類屬性是否有默認值,如果有則添加到結(jié)果字典中。
- __getattr__ 方法: 為了模擬問題描述中“未實例化屬性”的場景,重寫了 __getattr__ 方法,當訪問不存在的屬性時拋出 AttributeError。
運行結(jié)果:
{'parent_param': {'types': [<class 'str'>]}, 'parent_default': {'types': [<class 'str'>, <class 'NoneType'>], 'default': None}, 'child_param': {'types': [<class 'str'>]}
注意事項
- 類型覆蓋: 在繼承關系中,如果子類重新定義了父類的屬性,并且類型注解不同,則子類的類型注解會覆蓋父類的類型注解。
- 性能: 使用反射可能會影響性能,特別是在頻繁調(diào)用的場景中。 可以考慮緩存結(jié)果以提高性能。
- 兼容性: inspect.get_annotations 是 Python 3.9 引入的,如果需要兼容更早的版本,可以使用 typing.get_type_hints。
總結(jié)
通過結(jié)合 inspect.get_annotations 和 __mro__ 屬性,我們可以方便地獲取類及其父類的類型注解。這對于實現(xiàn)一些通用的工具函數(shù),例如自動生成 API 文檔、參數(shù)校驗等非常有用。 在實際應用中,需要注意類型覆蓋、性能和兼容性等問題。
? 版權聲明
文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載。
THE END