首页 > 解决方案 > 同时实现两个内部 Python 类型

问题描述

我正在尝试将函数的返回类型从更改setlist. 为了顺利过渡,我们的想法是进行就地弃用并临时返回一个既是 aset 是a的类型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 

否则我的想法是重写元类的__instancecheck__方法,ListSet类似于

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

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

在 Python 中这样的事情可能吗?

标签: pythontypesmetaclass

解决方案


因此,不,直接继承自setlist将使自定义类的实例返回“True”以进行isinstance调用。并且有几个原因导致一个类不能同时从两者继承,即使你在本机代码中编写这样一个类,或者用 ctypes 修改它,以便它会返回 True 进行这样的isinstance检查,结果class 可能会在使用时使您的 Python 解释器崩溃。

另一方面,如果被问及任何类的实例是否是使用这些方法的自定义类的实例,抽象基类和 、__instancecheck__和方法提供的机制允许回答“真”。也就是说:如果被问到,您的自定义类可以回答任意列表或集合是其自身的一个实例,例如 in - 但不是相反:将始终返回 False。__subclasscheck____subclasshook__registermyobj = set((1,2,3)); isinstance(myobj, MySpecialClass) -> Trueisinstance(MySpecialClass(), set)

为了允许类似的机制,推荐的是一种用于prococols的代码,而不是用于特定类的代码。有,任何编写良好的代码应该总是做isinstance(obj, collections.abc.Set)collections.abc.Sequence永远不会isinstance(..., set)(或list)。然后任何人都可以将自定义类注册为这些类的子类,测试将是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 的虚拟子类,以及任何行为良好的代码,通过 collections.abc.Sequence 实例检查测试协议,或者更好的是,只根据需要使用对象的代码允许不正确的对象在运行时失败,只会工作。如果您可以摆脱任何“isinstance”检查,只需编写“MutableSet”的适当实现即可为您提供一个“有序集”,它可以像您想要的那样工作,而不必担心对对象类型的任意检查。

这一点都不难:你可以只实现所需的方法,初始化一个包含你的类中实际数据 __init__的列表,更新对 Set 的所有内容修改调用的列表,并在列表上迭代

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”实例的硬编码测试,那么您无能为力。


推荐阅读