2009-04-14 12 views
38

En Python, el operador de asignación puede descomprimir lista o tupla en variables, como esto:Lista parcial de desempaquetado en Python

l = (1, 2) 
a, b = l # Here goes auto unpack 

pero tengo que especificar exactamente la misma cantidad de nombres a la izquierda como un conteo de artículos en la lista a la derecha. Pero a veces no conozco un tamaño de la lista a la derecha, por ejemplo, si uso split(). Ejemplo:

a, b = "length=25".split("=") # This will result in a="length" and b=25 

Pero el siguiente código dará lugar a un error:

a, b = "DEFAULT_LENGTH".split("=") # Error, list has only one item 

¿Es posible lista de desempaquetar alguna manera en el ejemplo anterior, así que llegar a = "DEFAULT_LENGTH" y B es igual a ' Ninguno 'o no establecido? Una manera sencilla parece larga:

a = b = None 
if "=" in string : 
    a, b = string.split("=") 
else : 
    a = string 
+0

https://stackoverflow.com/questions/5333680/extended-tuple-unpacking-in-python-2 general – n611x007

Respuesta

46
# this will result in a="length" and b="25" 
a, b = "length=25".partition("=")[::2] 

# this will result in a="DEFAULT_LENGTH" and b="" 
a, b = "DEFAULT_LENGTH".partition("=")[::2] 
-2

¿Has probado esto?

values = aString.split("=") 
if len(values) == 1: 
    a = values[0] 
else: 
    a, b = values 
+0

Debe ser a = valores [0] –

+0

o "a, = valores" –

7

Esto es ligeramente mejor que su solución, pero aún no es muy elegante; no me sorprendería si hay una mejor manera de hacerlo.

a, b = (string.split("=") + [None])[:2] 
+0

Niza. Básicamente una versión de partición de cosecha propia. –

4

Se podría escribir una función de ayuda para hacerlo.

>>> def pack(values, size): 
...  if len(values) >= size: 
...   return values[:size] 
...  return values + [None] * (size - len(values)) 
... 
>>> a, b = pack('a:b:c'.split(':'), 2) 
>>> a, b 
('a', 'b') 
>>> a, b = pack('a'.split(':'), 2) 
>>> a, b 
('a', None) 
0

No utilice este código, se entiende como una broma, pero hace lo que quiere:

a = b = None 
try: a, b = [a for a in 'DEFAULT_LENGTH'.split('=')] 
except: pass 
+1

¡Solo espere hasta que alguien intente extenderlo a trabajar para 3 variables (o use python3)! Poner eso en el código que alguien pueda leer sería bastante malo :-) Un enfoque más sensato es simplemente poner a = theString en el bloque except. – Brian

6

la mejor manera es usar el partition string method:

Split the string at the first occurrence of sep, and return a 3-tuple containing the part before the separator, the separator itself, and the part after the separator. If the separator is not found, return a 3-tuple containing the string itself, followed by two empty strings.

New in version 2.5.

>>> inputstr = "length=25" 
>>> inputstr.partition("=") 
('length', '=', '25') 
>>> name, _, value = inputstr.partition("=") 
>>> print name, value 
length 25 

También funciona para cadenas que no contengan =:

>>> inputstr = "DEFAULT_VALUE" 
>>> inputstr.partition("=") 
('DEFAULT_VALUE', '', '') 

Si por alguna razón está utilizando una versión de Python antes de 2.5, puede utilizar la lista sobre el tramo de hacer lo mismo, aunque un poco menos ordenadamente:

>>> x = "DEFAULT_LENGTH" 

>>> a = x.split("=")[0] 
>>> b = "=".join(x.split("=")[1:]) 

>>> print (a, b) 
('DEFAULT_LENGTH', '') 

..y cuando x = "length=25":

('length', '25') 

fácilmente convertido en una función o lambda:

>>> part = lambda x: (x.split("=")[0], "=".join(x.split("=")[1:])) 
>>> part("length=25") 
('length', '25') 
>>> part('DEFAULT_LENGTH') 
('DEFAULT_LENGTH', '') 
+0

Me gusta esta respuesta mejor que la seleccionada porque realmente explica qué hace la partición. La notación no era 100% intuitiva a primera vista. –

50

Esto puede ser de ninguna utilidad para usted a menos que estés usando Python 3. Sin embargo, por completo, vale la pena señalar que el extended tuple unpacking introducido no le permite hacer cosas como:

>>> a, *b = "length=25".split("=") 
>>> a,b 
("length", ['25']) 
>>> a, *b = "DEFAULT_LENGTH".split("=") 
>>> a,b 
("DEFAULT_LENGTH", []) 

es decir, El desempaquetado de tuplas ahora funciona de manera similar a como lo hace en el desempaquetado de argumentos, por lo que puede indicar "el resto de los elementos" con *, y obtenerlos como una lista (posiblemente vacía).

La partición es probablemente la mejor solución para lo que está haciendo sin embargo.

+2

py2 alternative? – n611x007

+0

Esta fue la respuesta que quería, gracias –

1

But sometimes I don't know a size of the list to the right, for example if I use split().

Sí, cuando tengo casos con límite de> 1 (así que no puedo usar la partición) Por lo general rolliza para:

def paddedsplit(s, find, limit): 
    parts= s.split(find, limit) 
    return parts+[parts[0][:0]]*(limit+1-len(parts)) 

username, password, hash= paddedsplit(credentials, ':', 2) 

(parts[0][:0] está ahí para obtener una str vacío' "o 'se han propuesto

0

Muchas otras soluciones, pero tengo que decir que el más sencillo para mí unicode', coincidente cualquiera de los de la división producida. usted podría utilizar Ninguno si lo prefiere.) sigue siendo

a, b = string.split("=") if "=" in string else (string, None) 
0

Como alternativa, ¿quizás use una expresión regular?

>>> import re 
>>> unpack_re = re.compile("(\w*)(?:=(\w*))?") 

>>> x = "DEFAULT_LENGTH" 
>>> unpack_re.match(x).groups() 
('DEFAULT_LENGTH', None) 

>>> y = "length=107" 
>>> unpack_re.match(y).groups() 
('length', '107') 

Si se asegura la re.match() siempre tiene éxito, .Grupos() siempre devuelve el número correcto de elementos para desembalar en su tupla, por lo que puede hacer con seguridad

a,b = unpack_re.match(x).groups() 
0

No recomiendo usar esto, pero solo por diversión aquí hay un código que realmente hace lo que quieres. Cuando llama al unpack(<sequence>), la función unpack utiliza el módulo inspect para encontrar la línea de origen real donde se llamó a la función, luego utiliza el módulo ast para analizar esa línea y contar el número de variables que se desempaquetar.

Advertencias:

  • Para la asignación múltiple (por ejemplo (a,b) = c = unpack([1,2,3])), sólo se utiliza el primer término de la asignación
  • no va a funcionar si no puede encontrar el código fuente (por ejemplo, porque se 're llamándolo desde el repl)
  • no funcionará si la instrucción de asignación se extiende por varias líneas

Código:

import inspect, ast 
from itertools import islice, chain, cycle 

def iter_n(iterator, n, default=None): 
    return islice(chain(iterator, cycle([default])), n) 

def unpack(sequence, default=None): 
    stack = inspect.stack() 
    try: 
     frame = stack[1][0] 
     source = inspect.getsource(inspect.getmodule(frame)).splitlines() 
     line = source[frame.f_lineno-1].strip() 
     try: 
      tree = ast.parse(line, 'whatever', 'exec') 
     except SyntaxError: 
      return tuple(sequence) 
     exp = tree.body[0] 
     if not isinstance(exp, ast.Assign): 
      return tuple(sequence) 
     exp = exp.targets[0] 
     if not isinstance(exp, ast.Tuple): 
      return tuple(sequence) 
     n_items = len(exp.elts) 
     return tuple(iter_n(sequence, n_items, default)) 
    finally: 
     del stack 

# Examples 
if __name__ == '__main__': 
    # Extra items are discarded 
    x, y = unpack([1,2,3,4,5]) 
    assert (x,y) == (1,2) 
    # Missing items become None 
    x, y, z = unpack([9]) 
    assert (x, y, z) == (9, None, None) 
    # Or the default you provide 
    x, y, z = unpack([1], 'foo') 
    assert (x, y, z) == (1, 'foo', 'foo') 
    # unpack() is equivalent to tuple() if it's not part of an assignment 
    assert unpack('abc') == ('a', 'b', 'c') 
    # Or if it's part of an assignment that isn't sequence-unpacking 
    x = unpack([1,2,3]) 
    assert x == (1,2,3) 
    # Add a comma to force tuple assignment: 
    x, = unpack([1,2,3]) 
    assert x == 1 
    # unpack only uses the first assignment target 
    # So in this case, unpack('foobar') returns tuple('foo') 
    (x, y, z) = t = unpack('foobar') 
    assert (x, y, z) == t == ('f', 'o', 'o') 
    # But in this case, it returns tuple('foobar') 
    try: 
     t = (x, y, z) = unpack('foobar') 
    except ValueError as e: 
     assert str(e) == 'too many values to unpack' 
    else: 
     raise Exception("That should have failed.") 
    # Also, it won't work if the call spans multiple lines, because it only 
    # inspects the actual line where the call happens: 
    try: 
     (x, y, z) = unpack([ 
      1, 2, 3, 4]) 
    except ValueError as e: 
     assert str(e) == 'too many values to unpack' 
    else: 
     raise Exception("That should have failed.") 
Cuestiones relacionadas