2012-04-24 10 views
36

En Python 3 Puedo hacer lo siguiente (véase también PEP3132 sobre el Extended Iterable desembalaje):cómo desembalar tupla de longitud n a m <n variables

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

¿Qué pueden hacer para lograr el mismo de manera similar elegante en Python 2.x?


sé que podría utilizar el acceso solo elemento y las operaciones de cortado, pero me pregunto si hay una más Pythonic manera. Mi código hasta ahora:

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

Parece que el corte explícito lo es, o el uso de varios vars _ underscore _ anónimos para capturar valores no deseados: x, _, _ = tup – jdi

+0

De hecho, tengo una pregunta sobre esta característica. ¿Cumple con el zen de python "Explicit is better than implicit."? – jdi

+1

@jdi declara explícitamente: consígame el primer artículo a 'a' y todos los demás artículos a' b'. Encuentro esto muy claro ... – moooeeeep

Respuesta

27

descubrí que la relacionada PEP3132 da algunos ejemplos para Python 2.x, así:

Muchos algoritmos requieren la división de una secuencia en un "primero, resto" par:

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

[...]

Además, si el valor de la derecha no es una lista, sino un iterable, se debe convertir a una lista antes de poder hacer un corte; para evitar la creación de esta lista temporal, se tiene que recurrir a

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

Otros enfoques dados en las respuestas a esta pregunta:

Función lista de argumentos Desembalaje Enfoque

requiere una definición/llamada de función extra:

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

Me pregunto por qué se implementa en las listas de argumentos de la función de desembalaje, pero no para el desempaquetado de tuplas normal.

Enfoque Generador

Credits. También requiere una implementación de función personalizada. Es un poco más flexible con respecto al número de primeras variables.

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) 

El más Pythonic sería probablemente los que se mencionan en el PEP anterior, supongo?

+1

Me pregunto lo mismo ... – jamylak

+3

A partir de hoy PEP 3132 solo parece aplicarse a Python 3.x . Puedo confirmar que la sintaxis 'first, * rest = seq' no funciona en Python 2.7.5 (mencionas 2.x arriba). –

+0

@Dave Gracias por traer esto de nuevo.Decidí eliminar la sección de la parte citada porque era una fuente de confusión y no era relevante para la respuesta. – moooeeeep

3

Puedo estar equivocado, pero por lo que yo sé

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

es sólo azúcar sintáctico para cortar en rodajas y la indexación de tuplas. Lo encuentro útil pero no muy explícito.

+1

Para ser honesto, todavía estoy en py2.7 y no he visto este 3.x Además todavía. Y ni siquiera parece mucho más útil que una porción. Obviamente sabes que quieres el primer elemento. Mucho prefiero el corte al azúcar. – jdi

+0

si bien puede ser simplemente azúcar, se vuelve más importante si su lista de la izquierda es mayor que 2 (bueno, tal vez más de 6 o 7). El problema con las rebanadas es que tienes que hacer un recuento manual en ambos lados del '=', lo que potencialmente hace que el código sea más difícil de mantener. –

+0

Tengo una lista de listas, cada una de las cuales tiene una longitud de 4 o más, y me gustaría poder hacerlo en python2.7: 'para a, b, c, d, * no utilizado en myList: '. Con nombres apropiados para a, b, c, d, me parece más expresivo que tener que indexar una sola variable. – mcmlxxxvi

4

No creo que hay alguna forma mejor que la que usted envió, pero aquí es una alternativa utilizando iter

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

No estoy seguro sobre el contexto, pero ¿qué pasa .pop (0)?

Veo que hay tuplas en tu ejemplo, pero si quieres hacer el tipo de cosas que haces, las listas serían más adecuadas, ¿no? (A menos que haya una buena razón para que sean inmutables no se da en la pregunta.)

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

Tengo esta pequeña práctica función:

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

Por ejemplo:

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

también funciona con menos argumentos:

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

En respuesta a la observación, también se puede definir:

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 

y utilizar de esta manera:

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

Eso me lleva a la idea: 'def foo (x, * y): devuelve x, y' y sería' a, b = foo (* (1,2,3)) '... – moooeeeep

+1

@moooeeeep Buena idea, pero puedes simplemente hacer 'a, b = foo (1,2,3)' en lugar de 'a, b = foo (* (1,2,3))' – jamylak

+0

@moooeeeep: ver la actualización)) – georg

Cuestiones relacionadas