web-dev-qa-db-fra.com

Concaténer deux résultats de fonction de plage

La fonction range permet-elle la concaténation? Comme je veux faire une range(30) & concaténer avec range(2000, 5002). Donc, ma gamme concaténée sera 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)
44
MAG

Vous pouvez utiliser itertools.chain pour cela:

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

Cela fonctionne pour les iterables arbitraires. Notez qu'il existe une différence de comportement entre range() entre Python 2 et 3 que vous devriez connaître: en Python 2 range renvoie une liste, et en Python3 un itérateur, efficace en termes de mémoire, toujours souhaitable.

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

48
Lev Levitsky

Peut être fait en utilisant 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]

Cela fonctionne pour votre demande, mais la réponse est longue et je ne l’afficherai pas ici.

remarque: peut être transformé en générateur pour des performances accrues:

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

ou même dans une variable génératrice.

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

J'aime les solutions les plus simples possibles (y compris l'efficacité). On ne sait pas toujours si la solution est telle. Quoi qu'il en soit, la range() de Python 3 est un générateur. Vous pouvez l'envelopper à n'importe quelle construction qui effectue l'itération. La list() est capable de construire une valeur de liste à partir de n'importe quelle valeur itérable. L'opérateur + 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) a fait exactement dans Python 2.5 - car range() a renvoyé une liste.

En Python 3, cela n’est utile que si vous voulez vraiment construire la liste. Sinon, je recommande la solution Lev Levitsky avec itertools.chain . La documentation montre également la 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 correcte et fonctionnellement équivalente. Quoi qu'il en soit, mon +1 va à Lev Levitsky et à son argumentation sur l'utilisation des bibliothèques standard. 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')

À 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

... c'est environ 3 fois plus vite.

26
pepr

Avec 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]
5
Sivachandran

Sur python3.5 +, vous pouvez utiliser le décompactage itérable dans les listes (voir PEP 448: Généralisations de décompression supplémentaires ).

Si vous avez besoin d'une liste,

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

Cela préserve l’ordre et ne supprime pas les doublons. Ou, vous voudrez peut-être un tuple,

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

... ou un ensemble,

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

Il se trouve également que cela est plus rapide que d’appeler 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, cependant, est que vous pouvez passer une liste arbitraire de plages.

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

OTOH, les généralisations de décompression n'ont pas été généralisées à des séquences arbitraires, vous devrez donc décompresser vous-même les plages individuelles.

4
cs95

range() dans Python 2.x renvoie une liste:

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

xrange() dans Python 2.x renvoie un itérateur:

>>> xrange(10)
xrange(10)

Et en Python 3, range() renvoie également un itérateur:

>>> r = range(10)
>>> iterator = r.__iter__()
>>> iterator.__next__()
0
>>> iterator.__next__()
1
>>> iterator.__next__()
2

Il est donc clair que vous ne pouvez pas concaténer des itérateurs autres en utilisant chain() comme l’a dit l’autre personne.

4
Andreas Jung

Je suis venu à cette question parce que j'essayais de concaténer un nombre inconnu de plages, qui pourraient se chevaucher, et je ne voulais pas de valeurs répétées dans l'itérateur final. Ma solution a été d'utiliser set et l'opérateur union comme suit:

range1 = range(1,4)
range2 = range(2,6)
concatenated = set.union(set(range1), set(range2)
for i in concatenated:
    print(i)
1
raphael

Je sais que c'est un peu vieux fil, mais pour moi, ce qui suit fonctionne.

>>> for i in range(30) + range(2000, 5002):
...    print i

Ainsi fait ceci 

>>> for i in range(30) , range(2000, 5002):
...  print i

Bien sûr, la sortie d’impression est différente entre le deuxième et le premier.

Edit: j’ai manqué le commentaire suivant dans le PO indiquant Python 3. C’est dans mon environnement Python 2.

0
R J