pythoncollectionssetmixins

Why does collections.MutableSet not bestow an update method?


When implementing a class that works like a set, one can inherit from collections.MutableSet, which will bestow the new class with several mixin methods, if you implement the methods they require. (Said otherwise, some of the methods of a set can be implemented in terms of other methods. To save you from this boredom, collections.MutableSet and friends contain just those implementations.)

The docs say the abstract methods are:

__contains__, __iter__, __len__, add, discard

and that the mixin methods are

Inherited Set methods and clear, pop, remove, __ior__, __iand__, __ixor__, and __isub__

(And, just to be clear that update is not part of the "Inherited Set methods, Set's mixin methods are:

__le__, __lt__, __eq__, __ne__, __gt__, __ge__, __and__, __or__, __sub__, __xor__, and isdisjoint

However, Set refers to an immutable set, which naturally would not have update.)

Why is update not among these methods? I find it surprising — unintuitive even — that set contains this method, but collections.Set does not. For example, it causes the following:

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'

Perhaps stranger is that MutableMapping does bestow an update method, while MutableSet does not. AFAICT, the source code does not mention any reason for this.


Solution

  • The API for MutableSet was designed by Guido van Rossum. His proposal is articulated in PEP 3119's section on for Sets. Without elaboration, he specified that:

    "This class also defines concrete operators to compute union, intersection, symmetric and asymmetric difference, respectively __or__, __and__, __xor__ and __sub__"

    ...

    "This also supports the in-place mutating operations |=, &=, ^=, -=. These are concrete methods whose right operand can be an arbitrary Iterable, except for &=, whose right operand must be a Container. This ABC does not provide the named methods present on the built-in concrete set type that perform (almost) the same operations."

    There is not a bug or oversight here; rather, there is a matter of opinion about whether you like or don't like Guido's design.

    The Zen of Python has something to say about that:

    That said, abstract base classes were designed to be easy to extend. It is trivial to add your own update() method to your concrete class with update = Set.__ior__.