为什么 dict 与 dict.keys() 联合返回一个集合?



正如我最初所料,dictset的并集给出了TypeError

>>> {1:2} | {3}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for |: 'dict' and 'set'

然而,令人惊讶的是,dictdict.keys()的并集返回一个set

>>> {1:2} | {3:4}.keys()
{1, 3}

set.union(dict)还具有以下行为:

>>> {3}.union({1:2})
{1, 3}

set | dict没有,表现得像dict | set

>>> {3} | {1:2}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for |: 'set' and 'dict'

这是怎么回事?为什么在某些情况下允许将字典和集合并集,但在其他情况下不允许,为什么在允许的情况下返回一组键?

字典视图是"类似集合的",但与set不同的是,它们没有(类型灵活)命名方法(如示例中的.union),它们只有运算符重载,这些重载保持类型灵活(因为命名方法不存在)。

由于类型灵活,它们与其他操作数一样与任何可迭代对象一起工作,并且dict是其键的可迭代对象(list({'a': 1, 'b': 2})['a', 'b']),因此在视图操作中忽略这些值。这并不是说dict在这里被特别接受,它们只是像任何其他可迭代对象一样被对待(你可以|字典视图,带有dictlisttuplerange、生成器或类似文件的对象,它们都可以工作,假设可哈希内容,并产生set结果)。

视图更加灵活也不错,因为它们不打算在操作后保留自己的类型,它们应该产生set输出。set的错位运算符更加严格,因为他们不想在确定输出类型时隐式地优先考虑左侧或右侧的类型(他们不希望set OP nonset留下任何关于结果是set类型还是nonset类型的疑问,并且他们不想让nonset OP set的行为可能不同)。由于字典视图无论如何都不会保留类型,因此他们决定对运算符重载采用更自由的设计;view OP nonviewnonview OP view的结果是一致的,而且总是set

支持这些特定操作的记录原因是为了匹配collections.abc.Set的功能:

对于类似集合的视图,为抽象基类collections.abc.Set定义的所有操作都可用(例如,==<^)。

出于某种原因,collections.abc.Set不需要任何命名方法(除了isdisjoint),只有运算符重载。

注意:我不同意这一点(我宁愿视图只让它们的运算符与其他视图和set/frozenset一起使用,并且为了保持一致性,视图也具有命名方法),但现在更改为时已晚,所以就是这样。

至于set方法的类型灵活,这更像是一种有意的选择。运算符不会给人一种强烈的印象,即其中一个操作数更重要,而方法必然更重要(你调用它的东西显然比参数更重要)。因此,这些方法接受任意可迭代对象(实际上,可以接受多个可迭代对象,如{1, 2, 3}.union(range(5), range(10, 15)))并返回调用它们的对象的类型,而运算符坚持要求类型同意以避免意外。

最新更新