2009-02-05 15 views
8

To, czego szukam, to najlepszy sposób na powiedzenie: "Jeśli ta lista jest zbyt krótka, wydłuż ją do 9 elementów i dodaj" Wybór 4 "," Wybór 5 ", itp., jako dodatkowe elementy. Również zamień elementy "Brak" na "Wybór x". " Można też zastąpić "" i 0.Pythonowy sposób na rozszerzenie potencjalnie niekompletnej listy

Przykładem transformacja byłaby

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

do

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

Mój początkowy kod nadużywane try/z wyjątkiem i miał błąd off-by-one nie zauważyłem; dzięki joeforkerowi i wszystkim, którzy to wskazali. Na podstawie uwag Próbowałem dwa krótkie rozwiązania sprawdzające się równie dobrze:

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) 

i

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

myślę # 2 wygranych jako bardziej pythonic, więc jest to jeden użyję. Łatwo jest zrozumieć i używa wspólnych konstrukcji. Dzielenie kroków jest logiczne i ułatwi zrozumienie na pierwszy rzut oka.

+0

Kiedy to odpowiedzieć, nie popełnia błąd numeracji od wyboru 0! – joeforker

+0

Jeśli wybrano pusty ciąg znaków "" lub 0, czy chcesz go pokazać użytkownikowi, czy też chcesz go traktować tak samo jak None? – joeforker

Odpowiedz

10

W przeciwieństwie do zip, Python map automatycznie rozszerza krótsze sekwencje z None.

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

Można by uprościć lambda do

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

, czy to w porządku, aby traktować innych fałszywie jak obiekty w choicesTxt taki sam jak None.

3

myślę, że zrobię coś bardzo podobne, ale z kilkoma czystymi upy:

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

Z czego tylko dwa są ważne tylko złapać IndexError zamiast wyjątku oraz od indeksu 0.

Jedynym prawdziwym problemem z oryginałem byłoby, gdy choicesTxt jest pusty, gdy dodane opcje zostaną wyłączone o jeden.

2

Myślę, że powinieneś traktować zmianę rozmiaru tablicy jako osobny krok. Aby to zrobić, w przypadku gdy tablica jest zbyt krótka, zadzwoń pod numer choicesTxt=choicesTxt+[None]*(10-len(choicesTxt)). Wyboru opcji None można dokonać za pomocą wyrażeń listowych.

0

Polecam również użycie xrange zamiast zakresu. Funkcja xrange generuje liczby w razie potrzeby. Range generuje je z góry. W przypadku małych zestawów nie ma dużej różnicy, ale w przypadku dużych zakresów oszczędności mogą być ogromne.

+1

W Pythonie 3.0 nie musimy się już o to martwić, ponieważ range zwraca iterator. – joeforker

1

Co o tym (zdaje się być DICT - nie lista - kiedy to niekompletne)

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

Edycja raz więcej: Jeśli to naprawdę lista (nie 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)] 

potrzebuje Python 2.5 Myślę (ze względu na operatora trójskładnikowego)?

(Dzięki joeforker ustalone, że używa klawiszy 1 do 10, a nie od 0 do 10; już dzięki SilentGhost: w bardziej pythonic niż has_key() lub len())

+0

ok, więc xrange (1,10) –

+0

.has_key() nie jest w ogóle pythonic – SilentGhost

+0

x w b nie ma sensu;) – SilentGhost

1

Jeśli nie przeszkadza zastępując cokolwiek, co ocenia Fałsz z "Wybór% d", wtedy result działa dla Pythona 2.4.

Jeśli nie masz nic przeciwko Pythonowi 2.5 i nowszym, użyj result2_5_plus z potęgą trójskładnikową if.

Jeśli nie podoba lub nie mogą korzystać z trójskładnikowej jeśli, to skorzystać z faktu, że True == 1 i False == 0, wykorzystując wynik x is None do indeksu na liście.

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

, ale wtedy ", 0 i [] otrzymują brzmienie" Wybór n ". To nie jest potrzebne, myślę, że –

+0

Może tego chcieć, pusty ciąg nie jest dobrym wyborem do pokazania użytkownikowi. – joeforker

7

Moja pierwsza reakcja była podzielona rozszerzenie listy i „wypełnienie luki” w oddzielnych częściach, jak tak:

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

Jeśli nie trzymać się z podejściem try...except, czy złapać specyficzny wyjątek jak pokazuje Douglas. W przeciwnym razie złapiesz wszystko, co: KeyboardInterrupts, RuntimeErrors, SyntaxErrors, .... Nie chcesz tego robić.

EDYCJA: naprawiony błąd listy 1-indeksowej - dzięki DNS!

EDIT: dodany alternatywny Lista rozszerzeń

+0

Druga pętla powinna zaczynać się od len (my_list) + 1; w innym przypadku pierwsza liczba będzie się nakładać. Na przykład, jeśli moja_lista to ["Wybór 1"], druga pętla to zakres (1, 10), który ponownie doda "Wybór 1", a wynikiem będzie lista 10 elementów, a nie 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

Można użyć mapy (słownika) zamiast listy:

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

wtedy masz mapę wypełnione danymi.
Jeśli chcesz listę zamiast można zrobić:

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

Przykładowa transformacja zachowuje pierwotne porządkowanie, sortowanie wartości() straciłoby to. Zamiast tego, opcje = [choice_map [k] dla k posortowane (choices_map.keys())] –

+0

Zakładając, że klucze w options_map odzwierciedlają kolejność oryginalnej listy. Ale masz rację, zaktualizuję swój post. Dzięki. – Abgan

1

Jestem trochę niejasne, dlaczego używasz zakres (1, 10); ponieważ używasz optionsTxt [i], który kończy się pomijaniem kontroli None dla pierwszego elementu na liście.

Są też oczywiście łatwiejsze sposoby, aby to zrobić, jeśli tworzysz nową listę, ale chcesz konkretnie dodać istniejącą listę.

Nie sądzę, że jest to czyściejsze lub szybsze, ale jest to dla ciebie inny pomysł.

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

powinno być 'v jest brakujące' – SilentGhost

+0

Masz na myśli choicesTxt [i] =" Choice "+ str (i + 1), jeśli v to None v?To uniemożliwiłoby False lub "" wywołanie go, ale wydaje się, że wypełnia listę wartościami niefałszywymi, więc może być bliżej tego, co chce. – DNS

1

zrobiłbym

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

który tylko zastępuje rzeczywiste wartości None (nie coś, co ocenia jako false) i rozszerza listę w oddzielnym kroku co moim zdaniem jest bardziej przejrzyste.

1

Uważam, że gdy rozumienie list jest długie, lepiej użyć standardowej pętli for. Prawie taki sam jak inni, ale w każdym razie:

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

Czy to Python czy Perl? –

1

Można pójść prostsze z listowego:

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

ten dołącza None do listy wyborów, dopóki nie ma co najmniej 10 pozycji, wylicza poprzez wynik i wstawia "Wybór X", jeśli brakuje X-tego elementu.

+0

To jest lepsze niż moje – SilentGhost

+0

Generalnie, gdy tylko napiszę pętlę for z jedną linijką kodu, myślę o tym, jak będzie wyglądało zrozumienie listy. – ojrac

+0

To prawda, ale podobał mi się algorytm: uzupełnij Nonesami i zamień je na później - bardziej suche – 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

najprostszych i najbardziej pythonic dla mnie jest:

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

Dobrze w jednej linii:

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

Mimo że zastąpi niczego, FALSE (np None " ", 0 itd.) Z" Wybór n ". Najlepiej zastąpić "a or 'Choice %d' % i" funkcją, jeśli tego nie chcesz.

Kluczową rzeczą jest to, że map z argumentem Brak można użyć do rozszerzenia listy do długości wymaganej z Brak w wymaganych miejscach.

porządniej (więcej pythonic) wersja będzie:

 
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

moje dwa centy ...

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

Jeśli jest OK, aby zastąpić dowolną wartość false, np '' lub 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'] 

Jeśli naprawdę można wymienić tylko na Brak

>>> 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'] 
Powiązane problemy