Python中使用第三方类、抽象类和工厂类时简化长类型提示的方法
解决多云密钥管理器抽象方法的类型提示冗余问题
当前开发的Python包需要对接Azure、GCP等云平台的密钥管理器,通过抽象类_SecretManager统一接口,工厂类创建对应实例。但抽象方法_get_secret_manager的类型提示存在问题:如果用Union枚举所有云客户端类型,扩展到大量云提供商时会非常繁琐;尝试将返回类型设为_SecretManager又会导致mypy报错,因为实际返回的是云服务商的客户端实例,并非自定义的_SecretManager子类。
解决方案:使用泛型(Generics)动态指定返回类型
通过Python的泛型特性,让抽象类支持动态定义_get_secret_manager的返回类型,每个子类只需指定对应云平台的客户端类型即可,无需修改抽象类的类型提示,完美解决扩展性问题。
修改后的完整代码
from __future__ import annotations from abc import ABC, abstractmethod from typing import TypeVar, Generic from google.cloud.secretmanager import SecretManagerServiceClient from azureml.core import Keyvault # 定义类型变量,用于表示不同云平台的密钥管理器客户端类型 T = TypeVar('T') class _SecretManager(ABC, Generic[T]): """ABC Class for Cloud Secret Managers like Keyvault or GCP Secret Manager""" def __init__(self) -> None: self.manager: T = self._get_secret_manager() @abstractmethod def _get_secret_manager(self) -> T: pass @abstractmethod def _get_secret(self, name: str) -> str: pass class _AzureKeyVault(_SecretManager[Keyvault]): """_SecretManager Subclass for getting secrets from azure keyvault""" def _get_secret_manager(self) -> Keyvault: return Keyvault() def _get_secret(self, name: str) -> str: return self.manager.get_secret(name) class _GCPSecretManager(_SecretManager[SecretManagerServiceClient]): """_SecretManager Subclass for getting secrets from GCP Secret Manager""" def _get_secret_manager(self) -> SecretManagerServiceClient: return SecretManagerServiceClient() def _get_secret(self, name: str) -> str: # 实际方法可能不同,这里仅作示例 return self.manager.fetch_secret(name) class _SecretManagerFactory: """Factory for creating _SecretManager""" def __init__(self) -> None: self._managers = {"azure": _AzureKeyVault, "gcp": _GCPSecretManager} def create_secret_manager(self, cloud_provider: str, **kwargs) -> _SecretManager: try: manager_cls = self._managers[cloud_provider] except KeyError: raise ValueError(f"No secret manager available for cloud provider: {cloud_provider}") return manager_cls(**kwargs)
关键改动说明
- 引入泛型支持:导入
TypeVar和Generic,定义类型变量T来代表任意云平台的客户端类型。 - 抽象类泛型化:
_SecretManager继承Generic[T],将self.manager的类型标注为T,_get_secret_manager的返回类型也设为T。 - 子类指定具体类型:每个子类继承
_SecretManager时,通过[具体客户端类型]指定泛型参数,比如_AzureKeyVault(_SecretManager[Keyvault]),同时在_get_secret_manager方法中明确返回对应类型。 - 优化工厂类:添加
cloud_provider的类型提示,完善错误信息,同时加入GCP的管理器映射。
这样修改后,mypy可以准确推断每个子类中self.manager的类型,不会再出现属性不存在的报错;后续扩展新的云提供商时,只需创建新的子类并指定对应客户端类型即可,完全不需要修改抽象类的类型提示,扩展性拉满。
内容的提问来源于stack exchange,提问作者Jens
相关产品推荐
相关产品推荐

