2012-12-31 33 views
33

Czy funkcja zakresu umożliwia konkatenację? Tak jak chcę, aby range(30) & połączyć go z range(2000, 5002). Więc moja łączone zakres będzie 0, 1, 2, ... 29, 2000, 2001, ... 5001Łączenie wyników dwóch funkcji zakresu

Code tak nie działa na moim najnowszym pytona (ver: 3.3.0)

range(30) + range(2000, 5002) 
+0

wersja python? –

+0

wersja mojego pytona jest wersja 3.3.0 .. Mam również zaktualizowane w moim pytaniu – MAG

+1

Co chcesz dostać w wyniku (jak w jakiego typu danych - zwykły listy, generator, coś innego)? Co chcesz zrobić z wynikiem? –

Odpowiedz

34

Można użyć do tego itertools.chain:

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

It działa dla dowolnych iteracji. Zauważ, że istnieje różnica w zachowaniu range() między Python 2 i 3, o której powinieneś wiedzieć: w Pythonie 2 range zwraca listę, aw Pythonie 3 jest iteratorem, który jest wydajny pod względem pamięci, ale nie zawsze jest pożądany.

Listy można łączyć z +, iteratory nie mogą.

3

range() w Pythonie 2.x zwraca listę:

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

xrange() w Pythonie 2.x zwraca iterator:

>>> xrange(10) 
xrange(10) 

aw Pythonie 3 range() również zwraca iterator:

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

Jest więc oczywiste, że nie można łączenie innych iteratorów za pomocą chain(), jak wskazał inny facet.

+1

Twoja odpowiedź jest pomocna, ale nie zapewnia rozwiązania. –

23

Można to zrobić za pomocą 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] 

Roboty żądanie, ale to długa odpowiedź, więc nie będę po to tutaj.

uwaga: może być wykonane do generatora dla zwiększenia wydajności:

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

lub nawet do zmiennej generatora.

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

+1 bez dodatkowych zależności – mkind

+0

@mkind dzięki, nienawidzę zależności, ludzie zawsze skakać i odpowiedzieć ton importu i bibliotek, ale nie są one zawsze dostępne i często razy zrobić to samo, co normalny kod nie tylko zawinięte w pakiecie, to nie magia. :) –

+7

Nie sądzę, że _nie używanie standardowych bibliotek_ jest cnotą samą w sobie, ale jest to dobra odpowiedź. –

17

Lubię najprostsze rozwiązania, które są możliwe (w tym efektywność). Nie zawsze jest jasne, czy takie jest rozwiązanie. W każdym razie, range() w Pythonie 3 jest generatorem. Możesz go zawinąć do dowolnej konstrukcji, która wykonuje iterację. list() jest w stanie zbudować wartość listy z dowolnej iterowalnej. Operator + dla list nie jest konkatenacją. Używam mniejsze wartości w przykładzie:

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

To co range(5) + range(10, 20) dokładnie robił w Pythonie 2.5 - bo range() powrócił listę.

W Pythonie 3 jest to przydatne tylko wtedy, gdy naprawdę chcesz skonstruować listę. W przeciwnym razie zalecam rozwiązanie Lev Levitsky's z itertools.chain.Dokumentacja pokazuje również bardzo prosty realizacji:

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

Rozwiązanie przez Inbar Rose jest w porządku i funkcjonalnie równoważne. W każdym razie moja +1 trafia do Leva Levitsky'ego i do jego argumentu na temat używania standardowych bibliotek. Od Zen Pythona ...

W obliczu dwuznaczności, odrzucić pokusę do odgadnięcia.

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

Moim zdaniem itertools.chain jest bardziej czytelny. Ale co tak naprawdę jest ważne ...

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

... to około 3 razy szybciej.

+0

to jest świetne, przyznaję się do porażki przez wydajnie zbudowane standardowe moduły biblioteczne. ale twój czas jest dziwny, na moim komputerze po wielu testach, stwierdziłem, że moje rozwiązanie jest tylko około 1,8 razy wolniejsze, w przeciwieństwie do 3 razy wolniej. ale wciąż jest wolniej. –

+0

Jest to na pewno zależne od sprzętu i może być również zależne od systemu operacyjnego. Użyłem mojego przestarzałego komputera z dwurdzeniowym procesorem AMD Athlon 64 X2 3800+ o częstotliwości 2,01 GHz z 3 GB pamięci. System operacyjny to Windows 7 Home Premium 64-bitowy (zarówno procesor, jak i wynik pamięci to tylko 4,9 - http://windows.microsoft.com/en-US/windows7/What-is-the-Windows-Experience-Index) . Nie jestem pewien co do implementacji Pythona. Załóżmy, że możesz mieć więcej rdzeni procesora. – pepr

2

Z pomocą metody rozszerzenia, możemy złączyć dwie listy.

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

To jest dla Pythona 2, PO używa Python 3. –

+1

Extend nie jest słowem kluczowym, jest to metoda na listach Pythona. – user7610

1

przyszedłem na to pytanie, ponieważ starałem się łączyć nieznaną liczbę zakresów, które mogą pokrywać, i nie chce wielokrotnych wartości w końcowej iteracyjnej. Moim rozwiązaniem było użycie operatora set i operatora union w następujący sposób:

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