2009-02-05 8 views
8

Ce que je cherche est la meilleure façon de dire: «Si cette liste est trop courte, l'allonger à 9 éléments et ajouter« Choice 4 »,« Choice 5 », etc, comme les éléments supplémentaires. De même, remplacez les éléments 'None' par 'Choice x'. ' C'est ok pour remplacer "" et 0 aussi.La façon la plus pythonique d'étendre une liste potentiellement incomplète

Une transformation exemple serait

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

à

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

Mon code initial abusé try/except et avait une erreur hors par un je n'ai pas remarqué; merci à joeforker et à tous ceux qui l'ont fait remarquer. Sur la base des commentaires que j'ai essayé deux solutions courtes qui testent aussi 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) 

et

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)) 

Je pense # 2 victoires comme étant plus pythonique, il est donc celui que je vais utiliser. C'est facile à comprendre et utilise des constructions communes. Le fractionnement des étapes est logique et faciliterait la compréhension en un coup d'œil.

+0

Lorsque vous répondez à cela, ne faites pas l'erreur de numérotation de choix 0! – joeforker

+0

Si un choix était la chaîne vide "" ou 0 voudriez-vous la montrer à l'utilisateur, ou voudriez-vous la traiter de la même manière que None? – joeforker

Répondre

10

Contrairement à zip, map de Python étend automatiquement des séquences plus courtes avec None.

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

Vous pouvez simplifier le lambda

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

si elle est d'accord pour traiter d'autres objets faux comme dans choicesTxt le même que None.

3

Je pense que je ferais quelque chose à peu près comme ça, mais avec quelques hauts bien rangé:

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

Dont les deux seuls sont importants pour n'attraper IndexError plutôt que toute exception, et l'indice de 0.

Et le seul vrai problème avec l'original serait si choicesTxt est vide, lorsque les choix ajoutés seront désactivés par un.

2

Je pense que vous devriez traiter le redimensionnement du tableau comme une étape distincte. Pour ce faire, dans le cas où le tableau est trop court, appelez choicesTxt=choicesTxt+[None]*(10-len(choicesTxt)). La détermination du choix None peut être effectuée à l'aide des listes de choix.

0

Je recommande également d'utiliser xrange au lieu de gamme. La fonction xrange génère les nombres selon les besoins. La gamme les génère à l'avance. Pour les petits ensembles, cela ne fait pas beaucoup de différence, mais pour de grandes distances, les économies peuvent être énormes.

+1

Dans Python 3.0, nous n'avons plus à nous inquiéter car la plage retourne un itérateur. – joeforker

1

Qu'en est-il que (semble être un dict - pas une liste - quand il est incomplète)

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)] 

Modifier une fois de plus: Si c'est vraiment une liste (pas dict):

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)] 

besoin de Python 2.5 Je pense (à cause de l'opérateur ternaire)?

(Merci à joeforker fixe qu'il utilise les touches 1 à 10 et de 0 à 10 plus, grâce à SilentGhost: en est plus pythonique que has_key() ou len())

+0

ok, donc xrange (1,10) –

+0

.has_key() n'est pas du tout pythonique – SilentGhost

+0

x en b n'a pas de sens;) – SilentGhost

1

Si vous ne me dérange pas en remplaçant tout ce qui évalue à False avec "Choix% d", alors result fonctionne pour Python 2.4.

Si cela vous dérange et que vous avez Python 2.5 et supérieur, alors utilisez result2_5_plus avec la puissance de ternary if.

Si vous ne souhaitez pas ou ne pouvez pas utiliser si ternaire, puis tirer profit du fait que True == 1 et False == 0, en utilisant le résultat de x is None pour indexer une liste.

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

mais alors '', 0 et [] sont remplacés par "Choix n". Ce n'est pas voulu je pense –

+0

Il pourrait le vouloir, la chaîne vide n'est pas un très bon choix à montrer à l'utilisateur. – joeforker

7

Ma première réaction a été de diviser l'extension de la liste et « remplir les blancs » en parties séparées comme si:

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 vous en tenir à l'approche try...except, n'attrapent une exception spécifique en tant que Douglas montre. Sinon, vous allez prendre tout: KeyboardInterrupts, RuntimeErrors, SyntaxErrors, .... Tu ne veux pas faire ça.

EDIT: erreur de liste 1 indexée fixe - merci DNS!

EDIT: ajouté l'extension liste alternative

+0

La deuxième boucle devrait commencer à len (my_list) + 1; sinon, le premier nombre se chevauchera. Par exemple, si my_list est ["Choice 1"], la deuxième boucle est range (1, 10), ce qui ajoutera à nouveau "Choice 1" et aboutira à une liste de 10 éléments, et non 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

Vous pouvez utiliser la carte (dictionnaire) au lieu de la liste:

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

Ensuite, vous avez rempli map vos données.
Si vous voulez une liste à la place que vous pouvez faire:

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

L'exemple de transformation conserve l'ordre original, le tri des valeurs() le perdrait. A la place, choices = [choices_map [k] pour k dans sorted (choices_map.keys())] –

+0

En supposant que les clés de choices_map reflètent l'ordre de la liste d'origine. Mais tu as raison, je vais mettre à jour mon post. Merci. – Abgan

1

Je suis un peu difficile de savoir pourquoi vous utilisez plage (1, 10); puisque vous utilisez choicesTxt [i], cela finit par ignorer la case None pour le premier élément de votre liste.

De plus, il y a évidemment des façons plus simples de le faire si vous créez une nouvelle liste, mais vous demandez spécifiquement d'ajouter à une liste existante.

Je ne pense pas que ce soit vraiment plus propre ou plus rapide, mais c'est une idée différente à laquelle vous devez penser.

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

devrait être 'v is None' – SilentGhost

+0

Vous voulez dire choicesTxt [i] =" Choice "+ str (i + 1) si v est None else v?Cela empêcherait False ou "" de le déclencher, mais il semble remplir une liste avec des valeurs non False, donc cela peut être plus proche de ce qu'il veut. – DNS

1

Je ferais

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)] 

qui ne remplace que None valeurs réelles (rien qui évalue comme faux), et étend la liste dans une étape séparée que je pense est plus claire.

1

Je trouve que lorsque la compréhension de la liste est longue, il est préférable d'utiliser un standard pour la boucle. presque la même que d'autres, mais de toute façon:

>>> 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

Est-ce Python ou Perl? –

1

Vous pouvez aller plus simple avec une compréhension de la liste:

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

Cette concatène None à votre liste de choix jusqu'à ce qu'il ait au moins 10 articles, énumère à travers le résultat, et insère "Choice X" si l'élément Xth est manquant.

+0

c'est mieux que le mien – SilentGhost

+0

En général, chaque fois que je me trouve en train d'écrire une boucle for avec une ligne de code, je pense à ce que cela donnerait comme une compréhension de liste. – ojrac

+0

c'est vrai, mais j'ai aimé l'algorithme: recharger avec Nones et les remplacer par la suite - plus sec – 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

plus simple et pythonique pour moi est:

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 une ligne:

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

Bien que remplacera tout ce qui permet d'évaluer False (par exemple None, ' ', 0 etc.) avec "Choix n". Il est préférable de remplacer le "a or 'Choice %d' % i" par une fonction si vous ne le souhaitez pas.

L'élément clé est que map avec un argument de None peut être utilisé pour étendre la liste à la longueur nécessaire avec None aux endroits requis.

Une version plus net (plus de pythonique) serait:

 
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

Mes deux cents ...

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

S'il est autorisé à remplacer une fausse valeur, par exemple '' ou 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 vous avez vraiment ne peut remplacer pour Aucun

>>> 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'] 
Questions connexes