Python 3.7.4继承FrozenSet[str]报错,Set[str]正常的技术问询
解决继承不可变集合(FrozenSet)时的实例化错误
这个问题我之前也碰到过,核心原因是你混淆了typing模块里的泛型抽象基类和Python内置的具体集合类型,咱们一步步拆解解决:
为什么继承FrozenSet[str]会报错?
你用的FrozenSet[str]是typing模块提供的泛型抽象基类,它的作用是给类型注解用的(用来表示一个存储字符串的不可变集合类型),而不是用来直接继承创建可实例化的类的。
当你尝试实例化S(['a'], 'a')时,Python会尝试调用底层对应的具体不可变集合类型frozenset的构造逻辑,但frozenset的__new__方法(负责创建实例的方法)只接受一个可迭代对象参数,而你的类定义了两个参数的__init__,这就导致了参数不匹配的TypeError。
而你继承Set[str]时能正常运行,是因为typing.Set会自动关联到内置的可变集合set,set的__init__初始化逻辑允许这种扩展,但这其实不是推荐的写法——因为typing里的抽象基类本来就不是用来做父类实例化的。
正确的继承实现方式(不可变集合+自定义属性)
要实现带自定义属性的不可变集合,你应该直接继承Python内置的frozenset类,并且注意不可变类型需要通过重写__new__方法来初始化自定义属性(因为__init__是在实例创建后执行,而不可变类型的核心数据在__new__阶段就已经固定了)。
基础实现代码
from typing import Iterable class S(frozenset): def __new__(cls, strs: Iterable[str], name: str): # 先调用父类的__new__创建frozenset实例 instance = super().__new__(cls, strs) # 给实例添加自定义的name属性 instance.name = name return instance # 测试使用 s = S(['a'], 'a') print(s) # 输出: frozenset({'a'}) print(s.name) # 输出: 'a' # 尝试修改集合会触发错误,符合不可变要求 # s.add('b') # 报错: AttributeError: 'S' object has no attribute 'add'
带泛型支持的实现(类型安全)
如果你需要保留泛型类型提示(明确集合存储的元素类型),可以结合Generic和TypeVar来实现:
from typing import Iterable, Generic, TypeVar T = TypeVar('T') # 定义类型变量 class S(frozenset, Generic[T]): def __new__(cls, items: Iterable[T], name: str): instance = super().__new__(cls, items) instance.name = name return instance # 使用时可以指定元素类型 s: S[str] = S(['a', 'b'], 'my_set') print(s.name) # 输出: my_set
总结一下
- 不要用
typing.FrozenSet或typing.Set作为父类来创建可实例化的自定义集合类,它们是给类型注解用的抽象基类 - 继承内置的
frozenset可以直接获得不可变集合的所有功能,重写__new__来处理自定义参数和属性 - 这种方式既满足了“不可修改集合”的需求,又通过继承实现了自定义扩展
内容的提问来源于stack exchange,提问作者Hrvoje Jurić
相关产品推荐
相关产品推荐

