Я хочу получить из генератора только столько элементов, сколько требуется.
В следующем коде
a, b, c = itertools.count()
Я получаю это исключение:
ValueError: too many values to unpack
Я видел несколько связанных вопросов, однако у меня нет нулевого интереса к остальным элементам из генератора, я только хочу получить столько, сколько я прошу, , не указав это количество заранее.
Мне кажется, что Python определяет количество элементов, которые вы хотите, но затем пытается попытаться прочитать и сохранить больше, чем это число (создание ValueError
).
Как я могу предоставить только столько элементов, сколько мне нужно, не передавая сколько элементов я хочу?
Чтобы помочь понять приблизительное поведение, я считаю, что это возможно, здесь пример кода:
def unpack_seq(ctx, items, seq):
for name in items:
setattr(ctx, name, seq.next())
import itertools, sys
unpack_seq(sys.modules[__name__], ["a", "b", "c"], itertools.count())
print a, b, c
Если вы можете улучшить этот код, пожалуйста, сделайте.
Ответ Alex Martelli предлагает мне, что байт op UNPACK_SEQUENCE
отвечает за ограничение. Я не понимаю, почему эта операция должна требовать, чтобы сгенерированные последовательности также должны точно соответствовать длине.
Обратите внимание, что Python 3 имеет разные синтаксисы распаковки, которые, вероятно, недействительны для технического обсуждения в этом вопросе.
Вам нужен глубокий байт-код hack - то, что вы запрашиваете, не может быть выполнено на уровне исходного кода Python, но (если вы согласны совершить конкретную версию и выпуск Python), может быть выполнимо, после обработки байт-кода после того, как Python скомпилировал его. Рассмотрим, например:
>>> def f(someit):
... a, b, c = someit()
...
>>> dis.dis(f)
2 0 LOAD_FAST 0 (someit)
3 CALL_FUNCTION 0
6 UNPACK_SEQUENCE 3
9 STORE_FAST 1 (a)
12 STORE_FAST 2 (b)
15 STORE_FAST 3 (c)
18 LOAD_CONST 0 (None)
21 RETURN_VALUE
>>>
Как вы видите, байт-код UNPACK_SEQUENCE 3
происходит без того, чтобы итератор someit
получал наименьшее указание на него (итератор уже был вызван!) - так что вы должны префикс его в байт-коде с помощью "получить точно N байтов", например:
>>> def g(someit):
... a, b, c = limit(someit(), 3)
...
>>> dis.dis(g)
2 0 LOAD_GLOBAL 0 (limit)
3 LOAD_FAST 0 (someit)
6 CALL_FUNCTION 0
9 LOAD_CONST 1 (3)
12 CALL_FUNCTION 2
15 UNPACK_SEQUENCE 3
18 STORE_FAST 1 (a)
21 STORE_FAST 2 (b)
24 STORE_FAST 3 (c)
27 LOAD_CONST 0 (None)
30 RETURN_VALUE
где limit
- ваша собственная функция, легко реализуемая (например, через itertools.slice
). Таким образом, исходная 2-байт-кодовая последовательность "load fast; call function" (как раз перед байт-кодом для распаковки) должна стать такой 5-байт-кодовой последовательностью с глобальным байтовым кодом нагрузки для limit
перед исходной последовательностью и последовательность load-const; call function
после него.
Конечно, вы можете реализовать это байт-код в декораторе.
В качестве альтернативы (для общности) вы можете работать над изменением исходного источника функции, например. путем разбора и изменения AST, и перекомпиляции в байтовый код (но это требует, чтобы источник был доступен во время украшения, конечно).
Это стоит того, чтобы использовать его? Абсолютно нет, конечно, - смешной объем работы за крошечный "синтаксический сахар". Тем не менее, это может быть поучительный проект, чтобы преуспеть в овладении хакерами в бат-кодах, взлом атак и другие трюки с черной магией, которые вам, вероятно, никогда не понадобятся, но, безусловно, здорово знать, когда вы хотите выйти за пределы языка простого мастера языка что у гуру мирового класса - действительно, я подозреваю, что те, кто мотивирован стать гуру, - это, как правило, люди, которые не могут не упустить очарование таких "механических механизмов языка"... и тех, кто на самом деле сделайте, чтобы этот высокий уровень был подмножеством достаточно мудрым, чтобы понять, что такие попытки "просто играют" и преследуют их как деятельность свободного времени (умственный эквивалент поднятия тяжестей, например, судоку или кроссворды; -), не позволяя им вмешиваться в важные задачи (обеспечивая ценность для пользователей, развертывая прочный, понятный, простой, хорошо исполняемый, хорошо проверенный, хорошо документированный код, чаще всего без даже малейший намек черной магии к ней; -).
Вам нужно убедиться, что количество элементов на обеих сторонах соответствует. Один из способов - использовать islice из модуля itertools:
from itertools import islice
a, b, c = islice(itertools.count(), 3)
Python не работает так, как вам хочется. В любом присваивании, например
a, b, c = itertools.count()
правая часть сначала оценивается, перед левой стороной. Правая сторона не может знать, сколько элементов находится в левой части, если вы не сообщите об этом.
Или используйте понимание списка, так как вы знаете нужное количество элементов:
ic = itertools.count()
a,b,c = [ic.next() for i in range(3)]
или даже проще:
a,b,c = range(3)