2012-04-24 4 views
36

In Python 3 ich folgende tun kann (siehe auch PEP3132 zum Extended Iterable Auspacken):Wie auspacken Tupel der Länge n zu m <n Variablen

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

Was kann ich tun, um das gleiche ähnlich elegant zu erreichen in Python 2.x?


Ich weiß, dass ich Einzelelementzugriff und Schneiden Operationen verwenden könnte, aber ich frage mich, ob es ein pythonic Weg. Mein Code so weit:

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

Scheint wie das explizite Slicing ist, oder mit mehreren anonymen _ Unterstrich vars, um unerwünschte Werte einzufangen: x, _, _ = tup – jdi

+0

Ich habe tatsächlich eine Frage zu diesem Feature. Entspricht es dem Zen von Pythons "Explizit ist besser als implizit"? – jdi

+1

@jdi es explizit besagt: Hol mir den ersten Gegenstand zu 'a' und alle anderen Gegenstände zu' b'. Ich finde das sehr klar ... – moooeeeep

Antwort

27

Ich fand heraus, dass die im Zusammenhang PEP3132 2.x einige Beispiele für Python gibt auch:

Viele Algorithmen erfordern eine Sequenz in einem "ersten, Rest" Paar Aufspaltung:

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

[...]]

Wenn der rechte Wert keine Liste, sondern eine iterierbare Zahl ist, muss er in eine Liste konvertiert werden, bevor Slicing ausgeführt werden kann. Erstellung dieser temporären Liste zu vermeiden, muss man auf diese Frage in den Antworten gegeben

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

Andere Ansätze greifen:

Funktion Argument Liste Auspacken Ansatz

erfordert eine zusätzliche Funktionsdefinition/Anruf:

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

Ich frage mich, warum es in Entpackungsfunktion Argumentlisten implementiert ist, aber nicht für normale Tupel Entpacken.

Generator Ansatz

Credits. Außerdem erfordert eine benutzerdefinierte Funktionsimplementierung. Ist ein wenig flexibler hinsichtlich der Anzahl der ersten Variablen.

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) 

Die pythonic wahrscheinlich die, die in dem PEP oben erwähnt seien, denke ich?

+1

Ich fragte mich das gleiche ... – jamylak

+3

Ab heute scheint PEP 3132 nur für Python 3.x zu gelten . Ich kann bestätigen, dass die 'first, * rest = seq'-Syntax in Python 2.7.5 nicht funktioniert (Sie erwähnen 2.x oben). –

+0

@Dave Danke, dass du es wieder gebracht hast.Ich beschloss, den Abschnitt aus dem zitierten Teil zu entfernen, weil er eine Quelle der Verwirrung war und für die Antwort nicht relevant war. – moooeeeep

3

ich falsch sein kann, aber soweit ich weiß,

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

ist nur syntaktischer Zucker zum Schneiden und Indexieren Tupel. Ich finde es nützlich, aber nicht sehr explizit.

+1

Um ganz ehrlich zu sein, ich bin immer noch auf py2.7 und habe dieses 3.x nicht gesehen Zusatz noch. Und es sieht nicht viel nützlicher aus als ein Stück. Du weißt offensichtlich, dass du das erste Element willst. Viel lieber schneiden Sie zum Zucker. – jdi

+0

Während es nur Zucker sein kann, wird es wichtiger, wenn Ihre Liste auf der linken Seite größer als 2 ist (naja, vielleicht größer als 6 oder 7). Das Problem mit Slices besteht darin, dass Sie auf beiden Seiten des '=' etwas manuell zählen müssen, was den Code schwieriger zu pflegen macht. –

+0

Ich habe eine Liste von Listen, von denen jede die Länge 4 oder mehr hat, und ich wünschte, ich könnte das in python2.7 tun: 'für a, b, c, d, * unbenutzt in myList: '. Mit geeigneten Namen für a, b, c, d finde ich es expressiver als eine einzelne Variable indizieren zu müssen. – mcmlxxxvi

4

Ich glaube nicht, dass es als die eine bessere Möglichkeit ist, dass Sie geschrieben, aber hier ist eine Alternative iter

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

Nicht sicher Zusammenhang mit, aber was .pop (0)?

Ich sehe, dass es Tupel in Ihrem Beispiel gibt, aber wenn Sie die Art von Sachen tun möchten, die Sie tun, wären Listen geeigneter, denke ich? (Es sei denn, es gibt einige gute Gründe für sie unveränderlich zu sein, nicht in der Frage gegeben.)

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

ich dieses handliche kleine Funktion haben:

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

Zum Beispiel:

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

arbeitet auch mit weniger Argumente:

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

Als Reaktion auf den Kommentar, können Sie auch festlegen:

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 

und es wie folgt verwendet werden:

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

Das bringt mich auf die Idee: 'def foo (x, * y): zurück x, y 'und es wäre' a, b = foo (* (1,2,3)) '... – moooeeeep

+1

@moooeeep Gute Idee, aber Sie können einfach 'a, b = foo (1,2,3)' anstelle von 'a, b = foo (* (1,2,3)) ' – jamylak

+0

@moooeeeep: siehe das Update)) – georg

Verwandte Themen