Python 为什么collections.MutableSet不授予更新方法?
当实现一个像集合一样工作的类时,可以从集合.MutableSet继承,如果您实现了新类所需的方法,它将赋予新类几个mixin方法。(换言之,集合的某些方法可以用其他方法实现。为了避免这种无聊感,Python 为什么collections.MutableSet不授予更新方法?,python,collections,set,mixins,Python,Collections,Set,Mixins,当实现一个像集合一样工作的类时,可以从集合.MutableSet继承,如果您实现了新类所需的方法,它将赋予新类几个mixin方法。(换言之,集合的某些方法可以用其他方法实现。为了避免这种无聊感,collections.MutableSet和friends只包含这些实现。) 假设抽象方法是: \uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,添加,放弃 而且mixin方法是 继承的Set方法和c
collections.MutableSet
和friends只包含这些实现。)
假设抽象方法是:
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
,添加
,放弃
而且mixin方法是
继承的Set
方法和clear
,pop
,remove
,\uuuuuuuuuuuuuuuuuuuuuu
,\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
,以及
(而且,为了明确,update
不是“继承的Set
方法”的一部分,Set
的混合方法是:
\uuuuuuuuuuuuuuuu
,\uuuuu lt\uuuuuuuuu
,\uuuuuu-ne\uuuuuuu
,\uuuuuuu-ge
,\uuu和
,\uuuu或
,/uuuuuuuuuuuuuuuu子代码>,,
但是,Set
指的是一个不可变的集合,它自然不会有update
)
为什么update
不在这些方法之列?我发现set
包含此方法,但collections.set
不包含此方法,这令人惊讶,甚至不直观。例如,它导致以下情况:
In [12]: my_set
Out[12]: <ms.MySet at 0x7f947819a5d0>
In [13]: s
Out[13]: set()
In [14]: isinstance(my_set, collections.MutableSet)
Out[14]: True
In [15]: isinstance(s, collections.MutableSet)
Out[15]: True
In [16]: s.update
Out[16]: <function update>
In [17]: my_set.update
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-17-9ed968a9eb18> in <module>()
----> 1 my_set.update
AttributeError: 'MySet' object has no attribute 'update'
[12]中的:我的_集
出[12]:
在[13]中:s
Out[13]:set()
在[14]中:isinstance(my_set,collections.MutableSet)
Out[14]:对
在[15]中:isinstance(s,collections.MutableSet)
Out[15]:对
在[16]:s.update中
出[16]:
在[17]中:my_set.update
---------------------------------------------------------------------------
AttributeError回溯(最近一次呼叫上次)
在()
---->1 my_set.update
AttributeError:“MySet”对象没有属性“update”
也许奇怪的是,MutableMapping
确实提供了一个update
方法,而MutableSet
没有。AFAICT没有提到这方面的任何原因。更新:
Raymond Hettinger自己回应说,如下所述,Set抽象基类使用运算符,而不是命名方法
原始答复:
Raymond Hettinger是基于MutableSet
抽象基类编写的(参见底部的代码blob)但是他没有使用update方法。相反,他使用update方法调用的|=
操作符。我不知道您的bug报告是否会得到任何支持,因为它可能会破坏先前存在的代码,而这些代码只期望当前的实现
但是,您可以编写一个抽象基类,该类确实希望该方法包含您坚持要实现的更多方法:
import abc
import collections
class MyMutableSet(collections.MutableSet):
@abc.abstractmethod
def update(self, other):
raise NotImplementedError
MyMutableSet.register(set)
然后进行以下工作:
>>> isinstance(set('abc'), MyMutableSet)
True
如果我们试图将新的抽象基类(参见下面的配方)子类化,而不是MutableSet
:
>>> s = OrderedSet()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class OrderedSet with abstract methods update
这样做不应该破坏已有的代码,但如果要这样做,还可以实现所有其他方法
雷蒙德的食谱,根据我们的目的改编:
import collections
# class OrderedSet(collections.MutableSet):
class OrderedSet(MyMutableSet):
def __init__(self, iterable=None):
self.end = end = []
end += [None, end, end] # sentinel node for doubly linked list
self.map = {} # key --> [key, prev, next]
if iterable is not None:
self |= iterable
def __len__(self):
return len(self.map)
def __contains__(self, key):
return key in self.map
def add(self, key):
if key not in self.map:
end = self.end
curr = end[1]
curr[2] = end[1] = self.map[key] = [key, curr, end]
def discard(self, key):
if key in self.map:
key, prev, next = self.map.pop(key)
prev[2] = next
next[1] = prev
def __iter__(self):
end = self.end
curr = end[2]
while curr is not end:
yield curr[0]
curr = curr[2]
def __reversed__(self):
end = self.end
curr = end[1]
while curr is not end:
yield curr[0]
curr = curr[1]
def pop(self, last=True):
if not self:
raise KeyError('set is empty')
key = self.end[1][0] if last else self.end[2][0]
self.discard(key)
return key
def __repr__(self):
if not self:
return '%s()' % (self.__class__.__name__,)
return '%s(%r)' % (self.__class__.__name__, list(self))
def __eq__(self, other):
if isinstance(other, OrderedSet):
return len(self) == len(other) and list(self) == list(other)
return set(self) == set(other)
if __name__ == '__main__':
s = OrderedSet('abracadaba')
t = OrderedSet('simsalabim')
print(s | t)
print(s & t)
print(s - t)
MutableSet的API是由Guido van Rossum设计的。他的建议在中详细阐述。没有详细说明,他指定:
“此类还定义了计算并集的具体运算符,
交叉、对称和非对称差异
__或uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
“这也是
支持就地变异操作|=,&=,^=,-=。这些是
其右操作数可以是任意可数的具体方法,
除了&=,其右操作数必须是容器。这是ABC
不提供内置混凝土集上存在的命名方法
执行(几乎)相同操作的类型。“
这里没有bug或疏忽,而是关于您是否喜欢Guido的设计的意见问题
Python的禅宗对此有一些说法:
- 应该有一个——最好只有一个——显而易见的方法来做到这一点
- 虽然这种方式一开始可能并不明显,除非你是荷兰人
也就是说,抽象基类的设计是为了易于扩展使用update=Set将方法添加到您的具体类中。这是一个问题还是一个错误报告?@AaronHall:哈!这是一个好问题。可能只是。我总是认为我的代码是问题所在,也许有人知道我不知道的东西。再看一遍,我不确定。(如果有原因的话,一个bug报告可能会找出原因。)归档。加上一个,但是经过进一步的思考,我认为你不会得到太多的关注。见下面我的答案。我想update
将在ABC上实现(基本上)def update(self,iterable):因为我在iterable:self.add(i)
;我不认为添加这样的定义会破坏任何东西(要么你没有,现在你有,要么你用自己的,可能更高效的版本覆盖它)@Thanatos当然,让我补充一下我的回答。嗯,用\uuuuuu操作符\uuuuu
函数实现命名方法通常是不安全的,因为操作符方法可能(而且通常应该)是这样的返回NotImplemented
,而不是在传递无效参数时引发异常。在这种情况下,它似乎是正常的,因为它似乎是MutableSet。如果传递了任何不可iterable但(理论上)是这不是\uuuuuuior\uuuuu
的最佳行为,因为它阻止了在中定义的\uuuuuRor\uuuuu
方法,该方法在提供的某些奇数不可编辑但可设置联合类型中定义
import collections
# class OrderedSet(collections.MutableSet):
class OrderedSet(MyMutableSet):
def __init__(self, iterable=None):
self.end = end = []
end += [None, end, end] # sentinel node for doubly linked list
self.map = {} # key --> [key, prev, next]
if iterable is not None:
self |= iterable
def __len__(self):
return len(self.map)
def __contains__(self, key):
return key in self.map
def add(self, key):
if key not in self.map:
end = self.end
curr = end[1]
curr[2] = end[1] = self.map[key] = [key, curr, end]
def discard(self, key):
if key in self.map:
key, prev, next = self.map.pop(key)
prev[2] = next
next[1] = prev
def __iter__(self):
end = self.end
curr = end[2]
while curr is not end:
yield curr[0]
curr = curr[2]
def __reversed__(self):
end = self.end
curr = end[1]
while curr is not end:
yield curr[0]
curr = curr[1]
def pop(self, last=True):
if not self:
raise KeyError('set is empty')
key = self.end[1][0] if last else self.end[2][0]
self.discard(key)
return key
def __repr__(self):
if not self:
return '%s()' % (self.__class__.__name__,)
return '%s(%r)' % (self.__class__.__name__, list(self))
def __eq__(self, other):
if isinstance(other, OrderedSet):
return len(self) == len(other) and list(self) == list(other)
return set(self) == set(other)
if __name__ == '__main__':
s = OrderedSet('abracadaba')
t = OrderedSet('simsalabim')
print(s | t)
print(s & t)
print(s - t)