同时实现两个内部 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 中有这样的可能吗?
所以,不,除了直接从 set
或 list
继承之外,没有什么会使自定义 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”实例的硬编码测试,您将无能为力。
我正在尝试将函数的 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 中有这样的可能吗?
所以,不,除了直接从 set
或 list
继承之外,没有什么会使自定义 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”实例的硬编码测试,您将无能为力。