Почему dict, объединенный с dict.keys (), возвращает набор? - PullRequest
4 голосов
/ 28 марта 2019

Как я и ожидал, объединение dict и 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'

Однако, как ни странно, объединение dict и dict.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'

Что здесь происходит? Почему в одних случаях разрешается объединение dict и set, но не в других, и почему он возвращает набор ключей в тех случаях, когда это разрешено?

1 Ответ

3 голосов
/ 28 марта 2019

Представления словаря «подобны множеству», но в отличие от set, они не имеют именованных методов (типа гибких) (например, .union, как в вашем примере), они просто имеют перегрузки операторов, которые остаются типамигибкие (так как названные методы не существуют).

Будучи гибкими по типу, они работают с any итерируемыми в качестве другого операнда, а dict s являются итерируемыми их ключами (list({'a': 1, 'b': 2}) равно ['a', 'b']), поэтому значения игнорируются в операции просмотра.Дело не в том, что dict здесь специально приняты, они просто обрабатываются как любые другие итерируемые (вы можете | представление словаря с генератором dict, list, tuple, rangeили подобный файлу объект, и все они работали бы, предполагая хешируемое содержимое, и в результате получали бы set).

Не так уж плохо для представлений быть более гибкими, потому что они ненамереваясь сохранить свой собственный тип после операции, ожидается, что они будут выдавать set выходных данных.Операторы set вне места более строги, потому что они не хотят неявно отдавать приоритет типу левой или правой стороны при определении типа вывода (они не хотят set OP nonsetоставить сомнение относительно того, имеет ли результат тип set или nonset, и они не хотят, чтобы nonset OP set вел себя по-другому).Поскольку представления словаря в любом случае не сохраняют типы, они решили пойти на более либеральный дизайн для своих перегрузок операторов;результат view OP nonview и 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))) и возвращают тип объекта, для которого они были вызваны, в то время как операторы настаивают на том, что типы соглашаются избегать неожиданностей.

...