Конкатенация результатов двух диапазонов
Позволяет ли функция диапазона конкатенации? Как я хочу сделать range(30)
и объединить его с помощью range(2000, 5002)
. Таким образом, мой конкатенированный диапазон будет 0, 1, 2, ... 29, 2000, 2001, ... 5001
Код, подобный этому, не работает на моем последнем python (ver: 3.3.0)
range(30) + range(2000, 5002)
Ответы
Ответ 1
Вы можете использовать itertools.chain
для этого:
from itertools import chain
concatenated = chain(range(30), range(2000, 5002))
for i in concatenated:
...
Он работает для произвольных итераций. Обратите внимание, что существует разница в поведении range()
между Python 2 и 3, о котором вы должны знать: в Python 2 range
возвращает список, а в Python3 - итератор, который эффективен с точки зрения памяти, но не всегда желателен.
Списки могут быть объединены с помощью +
, итераторы не могут.
Ответ 2
Может быть сделано с помощью list-comprehension.
>>> [i for j in (range(10), range(15, 20)) for i in j]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 15, 16, 17, 18, 19]
Работает для вашего запроса, но это длинный ответ, поэтому я не буду публиковать его здесь.
Примечание: можно сделать в генератор для повышения производительности:
for x in (i for j in (range(30), range(2000, 5002)) for i in j):
# code
или даже в переменную генератора.
gen = (i for j in (range(30), range(2000, 5002)) for i in j)
for x in gen:
# code
Ответ 3
Мне нравятся самые простые решения, которые возможны (включая эффективность). Не всегда ясно, является ли решение таким. Во всяком случае, range()
в Python 3 является генератором. Вы можете связать его с любой конструкцией, которая выполняет итерацию. list()
способен создавать значение списка из любой итерируемой. Оператор +
для списков выполняет конкатенацию. Я использую меньшие значения в примере:
>>> list(range(5))
[0, 1, 2, 3, 4]
>>> list(range(10, 20))
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> list(range(5)) + list(range(10,20))
[0, 1, 2, 3, 4, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
Это то, что range(5) + range(10, 20)
точно сделал в Python 2.5 - потому что range()
вернул список.
В Python 3 это полезно, только если вы действительно хотите создать список. В противном случае я рекомендую решение Льва Левицкого с помощью itertools.chain. Документация также показывает очень простую реализацию:
def chain(*iterables):
# chain('ABC', 'DEF') --> A B C D E F
for it in iterables:
for element in it:
yield element
Решение от Inbar Rose прекрасно и функционально эквивалентно. Во всяком случае, мой +1 идет к Льву Левицкому и его аргументу об использовании стандартных библиотек. Из дзен питона...
Перед лицом двусмысленности откажитесь от соблазна гадать.
#!python3
import timeit
number = 10000
t = timeit.timeit('''\
for i in itertools.chain(range(30), range(2000, 5002)):
pass
''',
'import itertools', number=number)
print('itertools:', t/number * 1000000, 'microsec/one execution')
t = timeit.timeit('''\
for x in (i for j in (range(30), range(2000, 5002)) for i in j):
pass
''', number=number)
print('generator expression:', t/number * 1000000, 'microsec/one execution')
На мой взгляд, itertools.chain
более itertools.chain
. Но что действительно важно...
itertools: 264.4522138986938 microsec/one execution
generator expression: 785.3081048010291 microsec/one execution
... это примерно в 3 раза быстрее.
Ответ 4
С помощью метода extend мы можем объединить два списка.
>>> a = list(range(1,10))
>>> a.extend(range(100,105))
>>> a
[1, 2, 3, 4, 5, 6, 7, 8, 9, 100, 101, 102, 103, 104]
Ответ 5
range()
в Python 2.x возвращает список:
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
xrange()
в Python 2.x возвращает итератор:
>>> xrange(10)
xrange(10)
И в Python 3 range()
также возвращается итератор:
>>> r = range(10)
>>> iterator = r.__iter__()
>>> iterator.__next__()
0
>>> iterator.__next__()
1
>>> iterator.__next__()
2
Итак, ясно, что вы не можете конкатенировать итераторы с помощью chain()
, как указал другой парень.
Ответ 6
Начиная с python3. 5+, вы можете использовать итеративную распаковку в списках (см. PEP 448: Дополнительные обобщения распаковки).
Если вам нужен список,
[*range(2, 5), *range(3, 7)]
# [2, 3, 4, 3, 4, 5, 6]
Это сохраняет порядок и не удаляет дубликаты. Или, возможно, вы хотите кортеж,
(*range(2, 5), *range(3, 7))
# (2, 3, 4, 3, 4, 5, 6)
... или набор,
# note that this drops duplicates
{*range(2, 5), *range(3, 7)}
# {2, 3, 4, 5, 6}
Это также происходит быстрее, чем вызов itertools.chain
.
from itertools import chain
%timeit list(chain(range(10000), range(5000, 20000)))
%timeit [*range(10000), *range(5000, 20000)]
738 µs ± 10.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
665 µs ± 13.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Преимущество chain
, однако, заключается в том, что вы можете передавать произвольный список диапазонов.
ranges = [range(2, 5), range(3, 7), ...]
flat = list(chain.from_iterable(ranges))
OTOH, распаковка обобщений не была "обобщена" на произвольные последовательности, поэтому вам все равно придется распаковывать отдельные диапазоны самостоятельно.
Ответ 7
Я пришел к этому вопросу, потому что пытался объединить неизвестное количество диапазонов, которые могли бы перекрываться, и не нуждались в повторных значениях в конечном итераторе. Мое решение заключалось в том, чтобы использовать set
и union
:
range1 = range(1,4)
range2 = range(2,6)
concatenated = set.union(set(range1), set(range2)
for i in concatenated:
print(i)
Ответ 8
Я знаю, что это немного старая тема, но для меня работает следующее.
>>> for i in range(30) + range(2000, 5002):
... print i
Так ли это
>>> for i in range(30) , range(2000, 5002):
... print i
Конечно, вывод на печать во втором отличается от первого.
Изменение: я пропустил последующий комментарий от OP, заявив Python 3. Это в моей среде Python 2.