4

正如我最初预期的那样, adict和 a的并集set给出TypeError

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

然而,令人惊讶的是, a和的dict并集dict.keys()返回 a 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'

这里发生了什么?为什么在某些情况下允许使用 dict 和 set 的并集,但在其他情况下不允许,为什么在允许的情况下返回一组键?

4

1 回答 1

3

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

由于类型灵活,它们可以将任何可迭代对象作为其他操作数使用,并且dicts 是它们的键(list({'a': 1, 'b': 2})is ['a', 'b'])的可迭代对象,因此在视图操作中会忽略这些值。并不是dicts 在这里被特别接受,它们只是被视为任何其他可迭代对象(您可以|使用带有dict, list, tuple, range, 生成器或类似文件的对象的字典视图,并且它们都可以工作,假设可散列的内容,并产生 aset作为结果)。

视图更灵活并没有那么糟糕,因为它们不打算在操作后保留自己的类型,它们应该产生set输出。set的异位运算符更严格,因为他们不想在确定输出类型时隐式地优先考虑左侧或右侧的类型(他们不想set OP nonset留下任何疑问结果是 typeset还是nonset,并且他们不想让 fornonset OP set行为不同)。由于字典视图无论如何都不会保留类型,因此他们决定为运算符重载采用更自由的设计。view OP nonviewand的结果nonview 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)))并返回它们被调用的对象的类型,而操作员坚持要求类型一致以避免意外。

于 2019-03-27T22:19:09.227 回答