2012-04-24 4 views
36

W Pythonie 3 mogę wykonać następujące czynności (patrz również PEP3132 Extended iterowalny rozpakowaniu):Jak rozpakować krotka długości n do m <n zmiennych

a, *b = (1, 2, 3) 
# a = 1; b = (2, 3) 

Co mogę zrobić, aby osiągnąć to samo podobnie elegancki w Pythonie 2.x?


wiem, że mogę korzystać z jednego elementu dostępu i operacji krojenia, ale zastanawiam się, czy istnieje bardziej pythonic sposób. Mój kod do tej pory:

a, b = (1, 2, 3)[0], (1, 2, 3)[1:] 
# a = 1; b = (2, 3) 
+3

Wygląda na to, że jest to wyraźne wycinanie lub użycie mnogich anonimowych znaków podkreślenia _ w celu przechwycenia niechcianych wartości: x, _, _ = tup – jdi

+0

Mam pytanie dotyczące tej funkcji. Czy jest zgodny z zen Pythona "Jawny jest lepszy niż niejawny."? – jdi

+1

@jdi to jednoznacznie stwierdza: przynieś mi pierwszy element do 'a' i wszystkie inne elementy do' b'. Uważam to za bardzo jasne ... – moooeeeep

Odpowiedz

27

I okazało się, że związane PEP3132 podaje kilka przykładów dla Pythona 2.x, a także:

Wiele algorytmów wymagają rozdzielania sekwencji w „pierwszy spoczynku” pary:

first, rest = seq[0], seq[1:] 

[...]

Ponadto, jeśli wartość prawostronna nie jest listą, ale wartością iteracyjną, musi zostać przekonwertowana na listę, zanim będzie można wykonać cięcie; aby uniknąć tworzenia tej tymczasowej liście, trzeba uciekać się do

it = iter(seq) 
first = it.next() 
rest = list(it) 

innych podejść przedstawionych w odpowiedzi na to pytanie:

Funkcja Argument Lista Rozpakowanie Podejście

wymagającej dodatkowa definicja funkcji/połączenie:

def unpack(first, *rest): 
    return first, rest 
first, rest = unpack(*seq) 

Zastanawiam się, dlaczego jest on zaimplementowany w listach argumentów funkcji rozpakowywania, ale nie w przypadku zwykłego rozpakowywania krotek.

Generator Podejście

Credits. Wymaga również implementacji funkcji niestandardowej. Jest nieco bardziej elastyczny pod względem liczby zmiennych najpierw.

def unpack_nfirst(seq, nfirst): 
    it = iter(seq) 
    for x in xrange(nfirst): 
    yield next(it, None) 
    yield tuple(it) 
first, rest = unpack_nfirst(seq, 1) 

Najbardziej pythonic będzie prawdopodobnie te wymienione w PEP powyżej, myślę?

+1

Zastanawia mnie to samo ... – jamylak

+3

Na dzień dzisiejszy PEP 3132 pojawia się tylko w Pythonie 3.x . Mogę potwierdzić, że składnia "first, * rest = seq" nie działa w Pythonie 2.7.5 (wspomniałeś 2.x powyżej). –

+0

@Dave Dzięki za przypomnienie tego ponownie.Zdecydowałem się usunąć sekcję z cytowanej części, ponieważ była źródłem nieporozumień i nie dotyczyła odpowiedzi. – moooeeeep

3

Może się mylę, ale o ile wiem

a, *b = (1, 2, 3) 

jest po prostu cukier syntaktyczny do krojenia i indeksowania krotek. Uważam, że jest przydatny, ale niezbyt wyraźny.

+1

Szczerze mówiąc, nadal jestem na py2.7 i nie widziałem tego 3.x dodatek jeszcze. I nawet nie wygląda o wiele bardziej użytecznie niż plasterek. Oczywiście wiesz, że chcesz pierwszy element. Dużo wolę krojenie w cukier. – jdi

+0

, podczas gdy może to być po prostu cukier, staje się ważniejsze, jeśli twoja lista po lewej jest większa niż 2 (dobrze, może większa niż 6 lub 7). Problem z plasterkami polega na tym, że musisz wykonać ręczne liczenie po obu stronach '=', co potencjalnie utrudni utrzymanie kodu. –

+0

Mam listę list, z których każda ma długość 4 lub więcej, i chciałbym móc to zrobić w python2.7: 'dla a, b, c, d, * nieużywane w myList: '. Z odpowiednimi nazwami dla a, b, c, d, uważam, że jest bardziej wyrazisty niż konieczność indeksowania pojedynczej zmiennej. – mcmlxxxvi

4

Nie sądzę, istnieje lepszy sposób niż ten, który pisał, ale tutaj jest alternatywą przy użyciu iter

>>> x = (1,2,3) 
>>> i = iter(x) 
>>> a,b = next(i), tuple(i) 
>>> a 
1 
>>> b 
(2, 3) 
2

nie wiesz o kontekście, ale co .pop (0)?

Widzę, że w twoim przykładzie są krotki, ale jeśli chcesz robić rzeczy, które robisz, listy będą bardziej odpowiednie, tak myślę? (Chyba, że ​​jest jakiś dobry powód, żeby być niezmienna nie podane w pytaniu.)

b = [1,2,3] 
a = b.pop(0) 
8

Mam to mało poręczne funkcję:

def just(n, seq): 
    it = iter(seq) 
    for _ in range(n - 1): 
     yield next(it, None) 
    yield tuple(it) 

na przykład:

a, b, c = just(3, range(5)) 
print a, b, c 
## 0 1 (2, 3, 4) 

współpracuje również z mniej argumentów:

a, b, c = just(3, ['X', 'Y']) 
print a, b, c 
## X Y() 

W odpowiedzi na komentarz, można również określić:

def take2(a, *rest): return a, rest 
def take3(a, b, *rest): return a, b, rest 
def take4(a, b, c, *rest): return a, b, rest 
... etc 

i używać go tak:

p = (1,2,3) 
a, b = take2(*p) 
print a, b 
## 1 (2, 3) 
+1

To prowadzi mnie do idei: 'def foo (x, * y): return x, y' i będzie to' a, b = foo (* (1,2,3)) '... – moooeeeep

+1

@moooeeeep Dobry pomysł, ale możesz po prostu zrobić 'a, b = foo (1,2,3)' zamiast 'a, b = foo (* (1,2,3))' – jamylak

+0

@moooeeeep: zobacz aktualizację)) – georg