Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/354.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 为什么collections.MutableSet不授予更新方法?_Python_Collections_Set_Mixins - Fatal编程技术网

Python 为什么collections.MutableSet不授予更新方法?

Python 为什么collections.MutableSet不授予更新方法?,python,collections,set,mixins,Python,Collections,Set,Mixins,当实现一个像集合一样工作的类时,可以从集合.MutableSet继承,如果您实现了新类所需的方法,它将赋予新类几个mixin方法。(换言之,集合的某些方法可以用其他方法实现。为了避免这种无聊感,collections.MutableSet和friends只包含这些实现。) 假设抽象方法是: \uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,添加,放弃 而且mixin方法是 继承的Set方法和c

当实现一个像集合一样工作的类时,可以从集合.MutableSet继承,如果您实现了新类所需的方法,它将赋予新类几个mixin方法。(换言之,集合的某些方法可以用其他方法实现。为了避免这种无聊感,
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)