98 votes

Concaténation des résultats de deux fonctions d'intervalle

La fonction range permet-elle la concaténation ? Par exemple, je veux faire un range(30) & le concaténer avec range(2000, 5002) . Ma gamme concaténée sera donc 0, 1, 2, ... 29, 2000, 2001, ... 5001

Un code comme celui-ci ne fonctionne pas sur mon dernier python (ver : 3.3.0)

range(30) + range(2000, 5002)

101voto

Lev Levitsky Points 25303

Vous pouvez utiliser itertools.chain pour cela :

from itertools import chain
concatenated = chain(range(30), range(2000, 5002))
for i in concatenated:
     ...

Il fonctionne pour des itérables arbitraires. Notez qu'il y a une différence dans le comportement de range() entre Python 2 et 3 que vous devez connaître : en Python 2 range renvoie une liste, et en Python3 un itérateur, ce qui est économe en mémoire, mais pas toujours souhaitable.

Les listes peuvent être concaténées avec + les itérateurs ne le peuvent pas.

60voto

pepr Points 4263

J'aime les solutions les plus simples possibles (y compris l'efficacité). Il n'est pas toujours évident de savoir si la solution est telle. Quoi qu'il en soit, la range() dans Python 3 est un générateur. Vous pouvez l'intégrer à n'importe quelle construction qui fait de l'itération. Le générateur list() est capable de construire une valeur de liste à partir de n'importe quel itérable. Les + pour les listes effectue la concaténation. J'utilise des valeurs plus petites dans l'exemple :

>>> 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]

C'est ce que range(5) + range(10, 20) exactement dans Python 2.5 -- parce que range() a renvoyé une liste.

Dans Python 3, elle n'est utile que si vous voulez vraiment construire la liste. Sinon, je recommande l'utilisation de la fonction Lev Levitsky's solution avec itertools.chain . La documentation présente également une mise en œuvre très simple :

def chain(*iterables):
    # chain('ABC', 'DEF') --> A B C D E F
    for it in iterables:
        for element in it:
            yield element

La solution de Inbar Rose est bien et fonctionnellement équivalent. Quoi qu'il en soit, mon +1 va à Lev Levitsky et à son argument sur l'utilisation des bibliothèques standard. De la part de Le zen de Python ...

Face à l'ambiguïté, refusez la tentation de deviner.

#!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')

A mon avis, le itertools.chain est plus lisible. Mais ce qui est vraiment important...

itertools: 264.4522138986938 microsec/one execution
generator expression: 785.3081048010291 microsec/one execution

... il est environ 3 fois plus rapide.

50voto

coldspeed Points 111053

Python >= 3.5

Vous pouvez utiliser le déballage itérable dans les listes (voir PEP 448) : Généralités supplémentaires sur le déballage ).

Si vous avez besoin d'une liste,

[*range(2, 5), *range(3, 7)]
# [2, 3, 4, 3, 4, 5, 6]

L'ordre est ainsi préservé et les doublons ne sont pas supprimés. Vous pouvez aussi vouloir un tuple,

(*range(2, 5), *range(3, 7))
# (2, 3, 4, 3, 4, 5, 6)

... ou un ensemble,

# note that this drops duplicates
{*range(2, 5), *range(3, 7)}
# {2, 3, 4, 5, 6}

Il s'avère également plus rapide que d'appeler 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)

L'avantage de chain est qu'il est possible de passer une liste arbitraire d'intervalles.

ranges = [range(2, 5), range(3, 7), ...]
flat = list(chain.from_iterable(ranges))

Par ailleurs, la généralisation de la décomposition n'a pas été "généralisée" aux séquences arbitraires, de sorte que vous devrez toujours décomposer vous-même les plages individuelles.

40voto

Inbar Rose Points 13033

Peut se faire en utilisant liste-compréhension .

>>> [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]

Fonctionne pour votre demande, mais c'est une longue réponse donc je ne la posterai pas ici.

note : peut être transformé en générateur pour augmenter les performances :

for x in (i for j in (range(30), range(2000, 5002)) for i in j):
    # code

ou même dans une variable du générateur.

gen = (i for j in (range(30), range(2000, 5002)) for i in j)
for x in gen:
    # code

9voto

fewtalks Points 333

A l'aide de la méthode extend, nous pouvons concaténer deux listes.

>>> 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]

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X