Ответ 1
List (set, dict) позволяет перевести на другую структуру кода из выражений генератора. Давайте посмотрим на установившееся понимание:
def f():
return {i for i in range(10)}
dis.dis(f.__code__.co_consts[1])
2 0 BUILD_SET 0
3 LOAD_FAST 0 (.0)
>> 6 FOR_ITER 12 (to 21)
9 STORE_FAST 1 (i)
12 LOAD_FAST 1 (i)
15 SET_ADD 2
18 JUMP_ABSOLUTE 6
>> 21 RETURN_VALUE
Сравните с эквивалентным выражением генератора:
def g():
return (i for i in range(10))
dis.dis(g.__code__.co_consts[1])
2 0 LOAD_FAST 0 (.0)
>> 3 FOR_ITER 11 (to 17)
6 STORE_FAST 1 (i)
9 LOAD_FAST 1 (i)
12 YIELD_VALUE
13 POP_TOP
14 JUMP_ABSOLUTE 3
>> 17 LOAD_CONST 0 (None)
20 RETURN_VALUE
Вы заметите, что если выражение генератора имеет yield
, то задание набора хранит значение непосредственно в наборе, который он создает.
Это означает, что если вы добавите выражение yield
в тело выражения генератора, оно обрабатывается неотличимо от yield
, которое язык создает для тела генератора; в результате вы получаете два (или более) значения за итерацию.
Однако, если вы добавите yield
в список (set, dict), то понимание преобразуется из функции, создающей список (set, dict) в генератор, который выполняет инструкции yield
, а затем возвращает построенный список (set, dict). {None}
в результате определения набора устанавливается набор, построенный из каждого из None
, который выражения yield
оценивают.
Наконец, почему Python 3.3 не создает {None}
? (Обратите внимание, что предыдущие версии Python 3 делают.) Это из-за PEP 380 (поддержка a.k.a. yield from
). До Python 3.3, a return
в генераторе есть SyntaxError: 'return' with argument inside generator
; поэтому наши yield
ing-понимания используют поведение undefined, но фактический результат кода операции RETURN_VALUE
заключается в том, чтобы просто генерировать другое (окончательное) значение из генератора. В Python 3.3 поддерживается явная поддержка return value
; a RETURN_VALUE
код операции приводит к повышению StopIteration
, что приводит к остановке генератора без создания конечного значения.