2009-02-05 12 views
8

Lo que estoy buscando es la mejor manera de decir: 'Si esta lista es demasiado corta, estírela a 9 elementos y agregue' Opción 4 ',' Opción 5 ', etc, como los elementos adicionales. Además, reemplace cualquier elemento 'Ninguno' con 'Elección x'. ' Está bien reemplazar "" y 0 también.La manera más piadosa de extender una lista potencialmente incompleta

Un ejemplo sería la transformación

['a','b',None,'c'] 

a

['a','b','Choice 3','c','Choice 5','Choice 6','Choice 7','Choice 8','Choice 9'] 

Mi código inicial abusado try/excepto y ha habido un error off-by-one No me di cuenta; gracias a joeforker y a todos los que lo señalaron. Sobre la base de los comentarios que he intentado dos soluciones cortas que ponen a prueba igualmente bien:

def extendChoices(cList): 
    for i in range(0,9): 
    try: 
     if cList[i] is None: 
     cList[i] = "Choice %d"%(i+1) 
    except IndexError: 
     cList.append("Choice %d"%(i+1) 

y

def extendChoices(cList): 
    # Fill in any blank entries 
    for i, v in enumerate(cList): 
    cList[i] = v or "Choice %s" % (i+1) 

    # Extend the list to 9 choices 
    for j in range(len(cList)+1, 10): 
    cList.append("Choice %s" % (j)) 

Creo # 2 victorias como más Pythonic, así que es el que voy a utilizar. Es fácil de entender y utiliza construcciones comunes. Dividir los pasos es lógico y facilitaría que alguien lo entienda de un vistazo.

+0

Al responder a esto, no cometa el error de numeración de elección 0! – joeforker

+0

Si una opción era la cadena vacía "" o 0, ¿le gustaría mostrarla al usuario, o le gustaría tratarla de la misma manera que Ninguna? – joeforker

Respuesta

10

A diferencia de zip, Python's map extiende automáticamente secuencias más cortas con None.

map(lambda a, b: b if a is None else a, 
    choicesTxt, 
    ['Choice %i' % n for n in range(1, 10)]) 

Usted podría simplificar la lambda para

map(lambda a, b: a or b, 
    choicesTxt, 
    ['Choice %i' % n for n in range(1, 10)]) 

si está bien para tratar otros objetos falsos como en choicesTxt lo mismo que None.

3

creo que me gustaría hacer algo más o menos así, pero con algunas subidas ordenado:

for i in range(0,10): 
    try: 
    if choicesTxt[i] is None: 
     choicesTxt[i] = "Choice %d"%i 
    except IndexError: 
    choicesTxt.append("Choice %d"%i) 

De los cuales, los dos únicos importantes son sólo para coger IndexError lugar de excepción, y al índice de 0.

Y el único problema real con el original sería si choicesTxt está vacío, cuando las opciones añadidas estarán desactivadas en uno.

2

Creo que debería tratar el redimensionamiento de la matriz como un paso separado. Para hacerlo, en el caso de que la matriz sea demasiado corta, llame al choicesTxt=choicesTxt+[None]*(10-len(choicesTxt)). La reasignación de opción None se puede hacer usando listas de comprensión.

0

También recomendaría utilizar xrange en lugar de rango. La función xrange genera los números según sea necesario. El rango los genera por adelantado. Para juegos pequeños no hace mucha diferencia, pero para rangos grandes, los ahorros pueden ser enormes.

+1

En Python 3.0 ya no tenemos que preocuparnos por esto porque range devuelve un iterador. – joeforker

1

¿Qué hay de que (parece ser un diccionario - no es una lista - cuando es incompleta)

a = {1:'a', 2:None, 5:'e'} #test data 
[a[x] if x in a and a[x] is not None else 'Choice %d'%x for x in xrange(1,10)] 

Editar una vez más: Si es realmente una lista (no un diccionario):

b=['a',None,'b'] 
[b[x] if len(b)>x and b[x] is not None else 'Choice %d'%x for x in xrange(10)] 

necesita Python 2.5 Creo que (debido al operador ternario)?

(Gracias a joeforker fijo que utiliza claves de 1 a 10 y no más de 0 a 10; gracias a SilentGhost: en que es más Pythonic has_key() o len())

+0

bien, entonces xrange (1,10) –

+0

.has_key() no es pythonic en absoluto – SilentGhost

+0

x en b no tiene sentido;) – SilentGhost

1

Si no le importa reemplazando todo lo que se evalúa como False con "Elección% d", entonces result funciona para Python 2.4.

Si te importa y tienes Python 2.5 y superior, entonces usa result2_5_plus con la potencia de ternary if.

Si no le gusta o no se puede utilizar si ternaria, entonces aprovechar el hecho de que True == 1 y False == 0, usando el resultado de x is None para indexar una lista.

x = ["Blue", None, 0, "", "No, Yelloooow!"] 
y = [None]*9 

result = [(t or "Choice %d" % (i+1))\ 
     for i, t in enumerate(x + y[len(x):])] 

result2_5_plus = [(t if t is not None else "Choice %d" % (i+1))\ 
     for i, t in enumerate(x + y[len(x):])] 

result_no_ternary_if = [[t, "Choice %d" % (i+1)][t is None]\ 
    for i, t in enumerate(x + y[len(x):])] 

['Blue', 'Choice 2', 'Choice 3', 'Choice 4', 'No, Yelloooow!', 'Choice 6', 'Choice 7', 'Choice 8', 'Choice 9'] 
['Blue', 'Choice 2', 0, '', 'No, Yelloooow!', 'Choice 6', 'Choice 7', 'Choice 8', 'Choice 9'] 
+0

pero luego '', 0 y [] se reemplazan por "Opción n". Eso no es necesario Creo que –

+0

Él podría quererlo, la cadena vacía no es una muy buena opción para mostrar al usuario. – joeforker

7

Mi reacción inicial fue dividir la extensión de lista y "llenar los espacios en blanco" en partes separadas como tan:

for i, v in enumerate(my_list): 
    my_list[i] = v or "Choice %s" % (i+1) 

for j in range(len(my_list)+1, 10): 
    my_list.append("Choice %s" % (j)) 

# maybe this is nicer for the extension? 
while len(my_list) < 10: 
    my_list.append("Choice %s" % (len(my_list)+1)) 

Si se pega con el enfoque try...except, atrapamos una excepción específica como Douglas muestra. De lo contrario, capturará todo: KeyboardInterrupts, RuntimeErrors, SyntaxErrors, .... No quieres hacer eso.

EDITAR: error de lista 1-indexado fijo - gracias DNS!

EDIT: lista alternativa añadido extensión

+0

El segundo ciclo debe comenzar en len (my_list) + 1; de lo contrario, el primer número se superpondrá. Por ejemplo, si my_list es ["Choice 1"], el segundo ciclo es range (1, 10), que agregará "Choice 1" nuevamente, y dará como resultado una lista de 10 elementos, no 9. – DNS

0
>>> in_list = ["a","b",None,"c"] 
>>> new = ['choice ' + str(i + 1) if j is None else j for i, j in enumerate(in_list)] 
>>> new.extend(('choice ' +str(i + 1) for i in range(len(new), 9))) 
>>> new 
['a', 'b', 'choice 3', 'c', 'choice 5', 'choice 6', 'choice 7', 'choice 8', 'choice 9'] 
3

Se puede usar un mapa (diccionario) en lugar de la lista:

choices_map = {1:'Choice 1', 2:'Choice 2', 3:'Choice 12'} 
for key in xrange(1, 10): 
    choices_map.setdefault(key, 'Choice %d'%key) 

Entonces has mapa lleno de sus datos.
Si desea una lista en lugar que puede hacer:

choices = choices_map.values() 
choices.sort() #if you want your list to be sorted 
#In Python 2.5 and newer you can do: 
choices = [choices_map[k] for k in sorted(choices_map)] 
+0

El ejemplo de transformación preserva el orden original, clasificando valores() perdería eso. En cambio, options = [choices_map [k] for k in sorted (choices_map.keys())] –

+0

Suponiendo que las claves en choices_map reflejen el orden de la lista original. Pero tienes razón, actualizaré mi publicación. Gracias. – Abgan

1

Estoy un poco claro por qué estás usando rango (1, 10); ya que está usando opcionesTxt [i], eso termina omitiendo la verificación None para el primer elemento en su lista.

Además, obviamente existen formas más sencillas de hacerlo si está creando una nueva lista, pero está solicitando específicamente que se agregue a una lista existente.

No creo que esto sea realmente más limpio o más rápido, pero es una idea diferente para que pienses.

for i, v in enumerate(choicesTxt): 
    choicesTxt[i] = v or "Choice " + str(i + 1) 

choicesTxt.extend([ "Choice " + str(i) for i in range(len(choicesTxt) + 1, 10) ]) 
+0

debe ser 'v es Ninguno' – SilentGhost

+0

Quiere decir opcionesTxt [i] =" Elección "+ str (i + 1) si v es Ninguna más v?Eso evitaría que False o "" lo desencadene, pero parece estar completando una lista con valores no falsos, por lo que puede estar más cerca de lo que quiere. – DNS

1

lo haría

for i, c in enumerate(choices): 
    if c is None: 
     choices[i] = 'Choice X' 

choices += ['Choice %d' % (i+1) for i in range(len(choices), 10)] 

que sólo reemplaza None valores reales (no todo lo que se evalúa como falsa), y se extiende la lista en un paso separado que creo que es más clara.

1

Me parece que cuando las listas de comprensión son largas, es mejor usar un ciclo estándar for. Casi lo mismo que otros, pero de todos modos:

>>> in_list = ["a","b",None,"c"] 
>>> full_list = in_list + ([None] * (10 - len(in_list))) 
>>> for idx, value in enumerate(full_list): 
...  if value == None: 
...    full_list[idx] = 'Choice %d' % (idx + 1) 
... 
>>> full_list 
['a', 'b', 'Choice 3', 'c', 'Choice 5', 'Choice 6', 'Choice 7', 'Choice 8', 'Choice 9', 'Choice 10'] 
1
choices[:] = ([{False: x, True: "Choice %d" % (i + 1)}[x is None] for i, x in enumerate(choices)] + 
    ["Choice %d" % (i + 1) for i in xrange(len(choices), 9)]) 
+1

¿Eso es Python o Perl? –

1

Usted podría ir más simple con una lista por comprensión:

extendedChoices = choices + ([None] * (10 - len(choices))) 
newChoices = ["Choice %d" % (i+1) if x is None else x 
    for i, x in enumerate(extendedChoices)] 

Este anexa None a su lista de opciones hasta que tenga por lo menos 10 artículos, enumera a través del resultado e inserta "Elección X" si falta el X elemento.

+0

que es mejor que el mío – SilentGhost

+0

En general, cada vez que me encuentro escribiendo un bucle for con una línea de código, pienso en cómo se vería como una lista de comprensión. – ojrac

+0

es cierto, pero me gustó el algoritmo: rellene con Nones y reemplácelos después - más seco – SilentGhost

1
def choice_n(index): 
    return "Choice %d" % (index + 1) 

def add_choices(lst, length, default=choice_n): 
    """ 
    >>> add_choices(['a', 'b', None, 'c'], 9) 
    ['a', 'b', 'Choice 3', 'c', 'Choice 5', 'Choice 6', 'Choice 7', 'Choice 8', 'Choice 9'] 
    """ 

    for i, v in enumerate(lst): 
    if v is None: 
     lst[i] = default(i) 

    for i in range(len(lst), length): 
    lst.append(default(i)) 

    return lst 

if __name__ == "__main__": 
    import doctest 
    doctest.testmod() 
2

más simple y más Pythonic para mí es:

repl = lambda i: "Choice %d" % (i + 1) # DRY 
print ([(x or repl(i)) for i, x in enumerate(aList)] 
    + [repl(i) for i in xrange(len(aList), 9)]) 
1

bien en una sola línea:

[a or 'Choice %d' % i for a,i in map(None,["a","b",None,"c"],range(10))]

A pesar de que sustituirá cualquier cosa que se evalúa como falso (por ejemplo: ninguno, ' ', 0 etc.) con "Opción n". Es mejor reemplazar el "a or 'Choice %d' % i" con una función si no quieres eso.

La clave es que map con un argumento Ninguno se puede utilizar para extender la lista a la longitud necesaria con Ninguno en los lugares requeridos.

A (más Pythonic) versión más ordenado sería:

 
def extend_choices(lst,length): 
    def replace_empty(value,index): 
     if value is None: 
      return 'Choice %d' % index 
     return value 
    return [replace_empty(value,index) for value,index in map(None,lst,range(length))] 

0

Mis dos centavos ...

def extendchoices(clist): 
    clist.extend([None]*(9-len(clist))) 
    for i in xrange(9): 
     if clist[i] is None: clist[i] = "Choice %d"%(i+1) 
0

Si está bien para sustituir a cualquier valor falso, por ejemplo '' o 0

>>> mylist = ['a','b',None,'c'] 
>>> map(lambda a,b:a or "Choice %s"%b, mylist, range(1,10)) 
['a', 'b', 'Choice 3', 'c', 'Choice 5', 'Choice 6', 'Choice 7', 'Choice 8', 'Choice 9'] 

Si realmente sólo se puede reemplazar por Ninguno

>>> map(lambda a,b:"Choice %s"%b if a is None else a, mylist, range(1,10)) 
['a', 'b', 'Choice 3', 'c', 'Choice 5', 'Choice 6', 'Choice 7', 'Choice 8', 'Choice 9'] 
0
>>> my_list=['a','b',None,'c'] 
>>> map (lambda x,y:x or 'Choice {}'.format(y+1),my_list,range(9)) 
['a', 'b', 'Choice 3', 'c', 'Choice 5', 'Choice 6', 'Choice 7', 'Choice 8', 'Choice 9'] 
Cuestiones relacionadas