如何让我的 Python `set` 和 `frozenset` 子类在进行二元运算时保留它们的类型?

How can I make my Python `set` and `frozenset` subclasses preserve their types when engaging in binary operations?

我分别有一些setfrozenset子类、OCDSetOCDFrozenSet。当我在二元运算中将它们与它们的祖先 类 的实例一起使用时,祖先 类 支配结果的类型——我的意思是,当我做类似减去 OCDFrozenSet 的事情时从 frozenset,我得到一个 frozenset… 但如果我在操作中反转类型也是如此(即从 OCDFrozenSet.[=22 中减去 frozenset =]

像这样:

… 令我特别反直觉的是,使用 -=(就地减法)会改变现有实例的类型!

我对如何处理这类事情的了解完全来自 C++,其中操作的类型是在(可能是模板化的)运算符重载函数中明确指定的已成定局;在 Python 中,类型系统通常更加隐含,但它并不像我现在相信的就地操作那样可变不可预测。

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

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

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

来自 library reference 就地运算符:

For immutable targets such as strings, numbers, and tuples, the updated value is computed, but not assigned back to the input variable.

同样 frozenset 文档也提到了同样的事情 about in-place operations[source]:

The following table lists operations available for set that do not apply to immutable instances of frozenset.


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

更重要的是,这是 bug in Python 2 where such operation returned the subclass instance, the fix was only ported to Python 3 虽然可以防止破坏现有系统。


要获得预期的输出,您可以在 subclass:

中提供所需的方法
class OCDFrozenSet(frozenset):
    def __sub__(self, other):
        return type(self)(super().__sub__(other))

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