同时实现两个内部 Python 类型

Implement two internal Python types simultaneuosly

我正在尝试将函数的 return 类型从 set 更改为 list。为了顺利过渡,我们的想法是进行就地弃用,并暂时 return 一种既是 set 又是 list 的类型.但是我不确定是否可以从两个内部 Python 类型派生,因为:

>>> class ListSet(list, set):
...     pass
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: multiple bases have instance lay-out conflict

总的来说,目标是拥有一个行为类似于排序集的类型(我认为它与列表和集合几乎相同)但也适用于类型检查(理想情况下也适用于 MyPy ):

thing = class.method_with_updated_return_type()
print(isinstance(thing, set))  # True
print(isinstance(thing, list))  # True 

否则我的想法是覆盖 ListSet 的元类的 __instancecheck__ 方法,类似于

class Meta(type):
    def __instancecheck__(self, instance):
        return isinstance(instance, (set, list))

class ListSet(metaclass=Meta):
    # implement all set and list methods 

在 Python 中有这样的可能吗?

所以,不,除了直接从 setlist 继承之外,没有什么会使自定义 class return 的实例成为 isinstance 呼叫。一个 class 不能同时从两者继承的原因有几个,即使你在本机代码中编写这样的 class 代码,或者用 ctypes 修改它,这样它会 return 对于这样的 isinstance 检查是正确的,结果 class 可能会在使用时使您的 Python 解释器崩溃。

另一方面,Abstract Base 类 和 __instancecheck____subclasscheck____subclasshook__register 方法提供的机制允许一个如果询问任何人 class 的实例是否是使用这些方法的自定义 class 的实例,请回答“真”。也就是说:你的自定义 class 可以回答,如果被问到,任意列表或集合是它自己的一个实例,就像在 myobj = set((1,2,3)); isinstance(myobj, MySpecialClass) -> True 中一样 - 但不是相反:isinstance(MySpecialClass(), set) 将永远return 错误。

为了允许类似的机制,推荐的是 prococols 的代码,而不是特定的 classes。有一点,任何写得很好的代码都应该总是 isinstance(obj, collections.abc.Set)collections.abc.Sequence,而不是 isinstance(..., set)(或 list)。然后任何人都可以将自定义 class 注册为它们的子 class,测试将是 True:

from collections.abc import MutableSet, Sequence

class MyClass(MutableSet):  
    #  <-NB. don't try to inherit _also_ from Sequence here. See bellow.


    # code mandatory methods for MutableSet according to
    # https://docs.python.org/3/library/collections.abc.html, 
    # plus customize all mandadory _and_ derivative methods
    # for a mutable sequence, in order to have your
    # desired "ordered mutable set" behavior here.

# After the class body, do:
Sequence.register(MyClass) 

调用 Sequence.register 会将 MyClass 注册为 Sequence 的虚拟子 class,以及通过 collections.abc.Sequence 测试协议的任何行为良好的代码实例检查,或者更好的是,仅在需要时使用对象的代码允许不正确的对象在运行时失败,将正常工作。我想你可以去掉任何“isinstance”检查,只要编写一个适当的“MutableSet”实现就可以给你一个“有序集”,它可以像你喜欢的那样工作,不用担心对对象类型的任意检查。

一点也不难:您只需实施所需的方法,初始化一个包含 class __init__ 中实际数据的列表,更新所有内容修改调用的列表集合,并在列表上迭代

from collections.abc import MutableSet

class OrderedSet(MutableSet):
    def __init__(self, initial=()):
        self.data = list()
        self.update(initial)
        
    def update(self, items):
        for item in items:
            self.add(item)
            
    def __contains__(self, item):
        return item in self.data
    
    def __iter__(self):
        return iter(self.data)
    
    def __len__(self):
        return len(self.data)
    
    def add(self, item):
        if item not in self.data:
            self.data.append(item)
        
    def discard(self, item):
        self.data.remove(item)

    def __repr__(self):
        return f"OrderedSet({self.data!r})"

但是,如果您不能更改“set”或“list”实例的硬编码测试,您将无能为力。