2015-04-27 10 views
12

Mam zadanie wymagające operacji na każdym elemencie listy, a wynik operacji w zależności od innych elementów na liście.Operate na liście w pyton sposób, gdy dane wyjściowe zależy od innych elementów

Na przykład chciałbym złączyć listę ciągów warunkowych na nich wychodząc ze szczególnym charakterze:

Kod ten rozwiązuje problem:

x = ['*a', 'b', 'c', '*d', 'e', '*f', '*g'] 
concat = [] 
for element in x: 
    if element.startswith('*'): 
     concat.append(element) 
    else: 
     concat[len(concat) - 1] += element 

powstałego w:

concat 
Out[16]: ['*abc', '*de', '*f', '*g'] 

Ale wydaje się to okropnie nie-Pythonic. Jak należy operować na elementach list, gdy wynik operacji zależy od wcześniejszych wyników?

Odpowiedz

13

Kilka odpowiednie fragmenty import this (arbiter, co jest pythonowy):

  • Proste jest lepsze niż złożonego
  • Readability liczy
  • Explicit jest lepszy niż niejawny.

Po prostu użyłbym takiego kodu i nie martw się o zamianę pętli for z czymś "bardziej płaskim".

x = ['*a', 'b', 'c', '*d', 'e', '*f', '*g'] 
partials = [] 
for element in x: 
    if element.startswith('*'): 
     partials.append([]) 
    partials[-1].append(element) 
concat = map("".join, partials) 
+4

Lovely. +1 za przywrócenie dyskusji do "* pythonic *" iz dala od zgrabnego/kompaktowego/szybkiego. – LondonRob

+0

Dodano twoją odpowiedź na mój mały benchmark, jeśli jesteś zainteresowany :) – miradulo

+0

Mój wolny czas na dużym wejściu mnie nie zaskakuje :) Jednakże, jeśli 'x' byłby naprawdę tak duży, spoglądałbym na budowanie' concat 'jak buduję' x', zamiast czekać, aż 'x' się zakończy. – chepner

4
"".join(x).split("*") 

może wystarczające, z grubej ten może być wymyślony przykład w OP, który jest uproszczony i jako takie nie będą działać

+2

Uzgodniono, że wygląda świetnie w tym konkretnym (ale nieco fikcyjnym) przykładzie. (Chociaż nie * całkiem * odtwarza wyjście, gwiazdy w OP są zachowane.) Czy jest jednak coś bardziej ogólnego, czego można się nauczyć o kodowaniu Python przez ten przykład? – LondonRob

+0

, że istnieje więcej niż jeden sposób na skórze kota? gdyby był mniej fikcyjny przykład Im na pewno moglibyśmy wymyślić coś równie czytelnego ... –

+0

Wybudowanie tego, co powiesz na 'list (map (lambda v: '*' + v," ".join (x). split ('*') [1:])) '? – onetwothree

7

Można użyć wyrażenia regularnego do osiągnięcia tego zwięźle. To jednak trochę obchodzi twoje pytanie dotyczące sposobu działania na elementach listy zależnej. Kredyty na numer mbomb007 w celu ulepszenia dozwolonej funkcjonalności postaci.

import re 
z = re.findall('\*[^*]+',"".join(x)) 

Wyjścia:

['*abc', '*de', '*f', '*g'] 

Mały benchmarking:

Donkey Kong's answer:

import timeit 
setup = ''' 
import re 
x = ['*a', 'b', 'c', '*d', 'e', '*f', '*g'] 
y = ['*a', 'b', 'c', '*d', 'e', '*f', '*g'] * 100 
''' 
print (min(timeit.Timer('re.findall("\*[^\*]+","".join(x))', setup=setup).repeat(7, 1000))) 
print (min(timeit.Timer('re.findall("\*[^\*]+","".join(y))', setup=setup).repeat(7, 1000))) 

Zwraca odpowiednio 0.00226416693456 i 0.06827958075.

Chepner's answer:

setup = ''' 
x = ['*a', 'b', 'c', '*d', 'e', '*f', '*g'] 
y = ['*a', 'b', 'c', '*d', 'e', '*f', '*g'] * 100 
def chepner(x): 
    partials = [] 
    for element in x: 
     if element.startswith('*'): 
      partials.append([]) 
     partials[-1].append(element) 
    concat = map("".join, partials) 
    return concat 
''' 
print (min(timeit.Timer('chepner(x)', setup=setup).repeat(7, 1000))) 
print (min(timeit.Timer('chepner(y)', setup=setup).repeat(7, 1000))) 

Zwraca 0.00456210269896 i 0.364635824689 odpowiednio.

Saksham's answer

setup = ''' 
x = ['*a', 'b', 'c', '*d', 'e', '*f', '*g'] 
y = ['*a', 'b', 'c', '*d', 'e', '*f', '*g'] * 100 

''' 
print (min(timeit.Timer("['*'+item for item in ''.join(x).split('*') if item]", setup=setup).repeat(7, 1000))) 
print (min(timeit.Timer("['*'+item for item in ''.join(y).split('*') if item]", setup=setup).repeat(7, 1000)))) 

Zwraca 0.00104848906006 i 0.0556093171512 odpowiednio.

tl; dr Saksham's jest nieco szybszy niż mój, po czym Chepner śledzi oba nasze.

+0

Uwaga: przed drugą gwiazdką opcja "' \ "jest opcjonalna, ponieważ jest zawarta w klasie znaków. Nie musisz go jednak edytować, jeśli nie chcesz. – mbomb007

6

Jak o tym:

>>> x = ['*a', 'b', 'c', '*d', 'e', '*f', '*g'] 
>>> print ['*'+item for item in ''.join(x).split('*') if item] 
['*abc', '*de', '*f', '*g'] 
+0

To jest najlepsza odpowiedź, moim zdaniem. +1 – miradulo

+0

hej to prawie tak samo jak moja odpowiedź: P on po prostu naprawia wynik, ale tak, +1 dobra odpowiedź imho :) –

+1

haha ​​jest rzeczywiście podobny do Jorana! –

3

czuję, że to jest bardzo pythonowy:

# assumes no empty strings, or no spaces in strings 
"".join(x).replace('*', ' *').split() 

Oto funkcjonalne podejście do niego:

from functools import reduce 

# assumes no empty strings 
def reduction(l, it): 
    if it[0] == '*': 
     return l + [it] 
    else: 
     new_l, last = l[:-1], l[-1] 
     return new_l + [last + it] 

x = ['*a', 'b', 'c', '*d', 'e', '*f', '*g'] 

print reduce(reduction, x, []) 
>>> ['*abc', '*de', '*f', '*g'] 

Jeśli jesteś fanem lambda (nie bardzo pythonic), można uciec z tego:

# Don't do this, it's ugly and unreadable. 
reduce(lambda l, it: l + [it] if it.startswith('*') else l[:-1] + [l[-1]+it], x, []) 
0

To jest strasznie blisko co itertools.groupby robi, aw rzeczywistości z małą porcją curry mogę to zrobić zachowaj grupowanie, aż pojawi się warunek "zerwania", na przykład startswith('*').

from itertools import groupby 

def new_group_when_true(pred): 
    group_num = 0 
    def group_for_elem(elem): 
     nonlocal group_num 
     if pred(elem): 
      group_num +=1 
     return group_num 
    return group_for_elem 

l = ['*a', 'b', 'c', '*d', 'e', '*f', '*g'] 

test = new_group_when_true(lambda elem: elem.startswith('*')) 

grouped = [list(v) for k,v in groupby(l, test)] 

Wynikające w:

>>> print(grouped) 
[['*a', 'b', 'c'], ['*d', 'e'], ['*f'], ['*g']] 

The nonlocal hasła wymaga Python 3, oczywiście. Inną możliwością byłoby stworzenie klasy, zgodnie z "równoważnym kodem" groupby z dokumentacji itertools.

nie wiem, że to jest więcej niż pythonowy kodzie, ale myślę, że pomysł iść do biblioteki standardowej, aby zobaczyć czy coś prawie pasuje do Twoich potrzeb jest dobrym punktem.

Powiązane problemy