首页 > 解决方案 > 在进行二进制操作时,如何让我的 Python `set` 和 `frozenset` 子类保留它们的类型?

问题描述

我分别有一些setfrozensetOCDSetOCDFrozenSet。当我在二进制操作中将它们与它们的祖先类的实例一起使用时,祖先类支配了结果的类型——我的意思是,当我执行OCDFrozenSet从 a中减去 an 之类的操作时frozenset,我得到 a frozenset...但是如果我反转操作中的类型(即frozensetOCDFrozenSet.

像这样:

在此处输入图像描述

……对我来说特别令人烦恼的是,使用-=(就地减法)会改变现有实例的类型!

我关于如何处理这类事情的知识严格来自 C++,其中操作的类型是已成定论的结论,它在(可能是模板化的)运算符重载函数中明确指定;在 Python 中,类型系统通常更加隐含,但它不像就地操作让我现在相信的那样不可预测。

那么,解决这个问题的最方便的方法是什么——我假设它涉及覆盖感兴趣的子类中的一些双下划线实例方法?

标签: pythontypesimmutabilitysubclassingbinary-operators

解决方案


就地操作不保证它们会就地更新对象,它完全取决于对象的类型。

Tuple、frozenset 等是不可变类型,因此无法就地更新它们。

从就地运算符的库参考中:

对于字符串、数字和元组等不可变目标,会计算更新的值,但不会将其分配回输入变量。

同样,frozenset文档也提到了关于就地操作[]的相同内容:

下表列出了不适用于 freezeset 的不可变实例的 set 可用操作。


现在,由于您OCDFrozenSet没有实现__isub__,它将回退到__sub__返回基类类型的方法frozenset。使用基类是因为 Python 不知道您的基类期望frozenset__sub__操作中新创建的参数。

更重要的是,这是Python 2 中的一个错误,其中此类操作返回了子类实例,该修复仅移植到 Python 3以防止破坏现有系统。


要获得预期的输出,您可以在子类中提供所需的方法:

class OCDFrozenSet(frozenset):
    def __sub__(self, other):
        return type(self)(super().__sub__(other))

    def __rsub__(self, other):
        return type(self)(super().__rsub__(other))

推荐阅读