You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义类型无法使用|联合类型语法的解决方案咨询

问题

我正在编写带生成器的Int、Str、Bool等自定义类,用来基于类型注解做函数模糊测试。目前一切正常,但用|表示联合类型时出错:

def test_handles_none_with_arged_types(
    x: Int[0, 10] | List[Int] | Str | Dict[NoneType, List[NoneType]]
):
    assert type(x) in [int, list, str, dict, list]
    if type(x) == int:
        assert x >= 0 and x <= 10
    if type(x) == list:
        assert all([el is None for el in x])
    if type(x) == dict:
        for k, v in x.items():
            assert k is None
            assert type(v) == list
            for el in v:
                assert el is None 

Python抛出错误:

TypeError: unsupported operand type(s) for |: 'Int' and 'List'

原因是Int[0,10]的类型是pybt.typing.Int而非type,但用typing.Union就能正常运行:

def test_handles_none_with_arged_types(
    x: Union[Int[0, 10], List[Int], Str, Dict[NoneType, List[NoneType]]]
):
    ... 

找不到延迟实例化Int等在__class_getitem__中被索引类型的方法,求解决办法。

附List类完整代码:

class List:
    def __init__(self, sub_type=_DEFAULT_SUB_TYPE, max_len=_DEFAULT_MAX_LEN):
        self.max_len: int = _DEFAULT_MAX_LEN
        self.sub_type = sub_type
        if max_len is not None:
            self.max_len = max_len

    def __str__(self):
        return "pybt.types.List"

    def __class_getitem__(cls, parameters):
        sub_type = None
        max_len = None
        if type(parameters) != tuple:
            parameters = (parameters,)

        if len(parameters) > 2:
            raise TypeError("Expected 2 arguments: List[sub_type, max_length]")
        if len(parameters):
            sub_type = parameters[0]
        if len(parameters) > 1:
            max_len = parameters[1]

        if max_len and max_len <= 0:
            raise TypeError(f"Max Length of {cls.max_len} is less than or equal to 0")

        return cls(sub_type, max_len)

解决方案

方法1:给自定义类实现__or__和__ror__方法

让Int、List等自定义类型支持|运算符,返回Union对象,直接兼容PEP 604的联合类型语法。以List类为例修改:

from typing import Union

class List:
    # 保留原有__init__、__str__、__class_getitem__方法
    
    def __or__(self, other):
        return Union[self, other]
    
    def __ror__(self, other):
        return Union[other, self]

这样Int[0,10] | List[Int]会直接返回Union[Int[0,10], List[Int]],和使用Union的效果完全一致。

方法2:让自定义类继承自type

如果你的自定义类型本质是模拟类型语义,可以让它们继承type(元类),这样天然支持|运算符(PEP 604对type实例原生支持该语法)。调整List类实现:

class List(type):
    def __new__(cls, sub_type=_DEFAULT_SUB_TYPE, max_len=_DEFAULT_MAX_LEN):
        instance = super().__new__(cls, 'List', (), {})
        instance.max_len = max_len if max_len is not None else _DEFAULT_MAX_LEN
        instance.sub_type = sub_type
        return instance
    
    def __class_getitem__(cls, parameters):
        sub_type = None
        max_len = None
        if not isinstance(parameters, tuple):
            parameters = (parameters,)

        if len(parameters) > 2:
            raise TypeError("Expected 2 arguments: List[sub_type, max_length]")
        if len(parameters):
            sub_type = parameters[0]
        if len(parameters) > 1:
            max_len = parameters[1]

        if max_len and max_len <= 0:
            raise TypeError(f"Max Length of {max_len} is less than or equal to 0")

        return cls(sub_type, max_len)

方法3:用包装器实现延迟实例化

如果需要严格延迟Int、List等类的实例化,可创建包装器类,在__class_getitem__中返回包装器而非直接实例化,直到需要使用时才完成实例化:

from typing import Union

class TypeWrapper:
    def __init__(self, cls, *args, **kwargs):
        self.cls = cls
        self.args = args
        self.kwargs = kwargs
        self._instance = None
    
    def get_instance(self):
        if self._instance is None:
            self._instance = self.cls(*self.args, **self.kwargs)
        return self._instance
    
    def __or__(self, other):
        left = self.get_instance()
        right = other.get_instance() if isinstance(other, TypeWrapper) else other
        return Union[left, right]
    
    def __ror__(self, other):
        left = other.get_instance() if isinstance(other, TypeWrapper) else other
        right = self.get_instance()
        return Union[left, right]

class List:
    # 保留原有__init__、__str__方法
    
    def __class_getitem__(cls, parameters):
        sub_type = None
        max_len = None
        if not isinstance(parameters, tuple):
            parameters = (parameters,)

        if len(parameters) > 2:
            raise TypeError("Expected 2 arguments: List[sub_type, max_length]")
        if len(parameters):
            sub_type = parameters[0]
        if len(parameters) > 1:
            max_len = parameters[1]

        if max_len and max_len <= 0:
            raise TypeError(f"Max Length of {max_len} is less than or equal to 0")

        return TypeWrapper(cls, sub_type, max_len)

内容的提问来源于stack exchange,提问作者dvr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 02:52:48