2012-07-12 20 views
9

Co jest lepszą optymalizacją?Lepsza technika optymalizacji za pomocą if/else lub dictionary

  • Seria instrukcji if/else, która odbiera ciąg znaków, zwraca dla niego odpowiednią funkcję. (Około 40-50 instrukcji if/else).
  • Słownik utrzymujący parę klucz-wartość. klucz jako łańcuchy i wartości jako obiekty funkcji oraz jedna główna funkcja wyszukiwania i zwracania obiektu funkcji.

Główna funkcja, która faktycznie zwraca obiekt funkcji za pomocą powyższej metody, byłaby nazywana milionami lub miliardami razy, więc trzeba to zrobić inteligentnie. Jaki może być lepszy sposób?

Dla np.

dict['str1'] = func1 
dict['str2'] = func2 
and so on.. 

def main_func(str): 
    return dict[str] 

Albo

def main_func(str): 
    if 'str1': 
     return func1 
    elif 'str2': 
     return func2 

Który będzie lepiej ..? jeśli mamy 50-60 takich ciągów, a proces ten musi być miliardy razy.

Przechowywanie obiektu funkcji wewnątrz słownika, w samej funkcji: -

def func1(): 
    if dict.has_key('str1'): 
     dict['str1'] = func1 
    -- do something -- 

Który jest lepszy, ten jeden lub wyżej. Wygląda to znacznie czystsze.? Pamiętaj jednak, że funkcje te byłyby wywoływane wiele razy, więc funkcja has_key również byłaby wywoływana wiele razy.

Dzięki

+10

Napisz zarówno i ** profiluj je **. – huon

+2

Idę ze słownikiem, ale zamiast sprawdzać najpierw, czy klucz jest w dyktafonie, wystarczy mieć funkcję, która wykona to, co należy zrobić, jeśli brakuje napisu i zwrócić dict.get (string, string_missing_function) – Paddy3118

+0

Dobra rozmowa! Każdego dnia ucz się czegoś nowego, włączyłem twój kawałek do mojego posta. To zaczyna pokazywać, że function_lookup nie potrzebuje nawet zdefiniowanego! –

Odpowiedz

12

Wybierz słownik.

Słownik ...

  • jest wbudowany w
  • jest pythonic
  • wymaga kod mniej szablonowe
  • jest O (1) złożoność w porównaniu z innym, jeżeli-liniowy O (n) złożoność
  • nie jest winni przedwczesnej pesymizacji (nie mamy wystarczających powodów, by sądzić bez profilowania, że ​​jest to metoda mniej skuteczna z dużym marginesem).

Proponuję napisać najpierw użyj słownika i sprawdź, czy rozwiązanie jest wystarczająco szybkie dla twoich potrzeb. Jeśli tak, to świetnie, gotowe. Jeśli nie, pomnóż to w przeciwną stronę.

rozważyć rozwiązanie takiego (który powróci None jeśli ciąg nie zostanie znaleziony):

func_dict = {} 
func_dict['str1'] = fun1 
func_dict['str2'] = fun2 
... 
def function_lookup(func_string): 
    return func_dict.get(func_string) 

Następnie w głównym, wystarczy napisać function_lookup(whatever_string_variable) próbować odnośnika do swojej funkcji. W ten sposób unika się przebudowy słownika za każdym razem, gdy wywoływany jest kod function_lookup.

+0

hmm, czy powinienem przechowywać obiekt funkcji wewnątrz samej funkcji, czy też zrobić to tylko w funkcji głównej, przykład jest napisany powyżej. – geek

+0

Dopóki nie określisz funkcji w pętli, powinieneś być dobry. –

+0

To byłaby ... funkcja byłaby wywoływana wiele razy .... tak będzie wywoływana funkcja has_key, ?? Czy mam rację..? – geek

2

Technicznie, to zależy od wyników kolizji hash, ale chciałbym sobie wyobrazić przechowywania wszystkich danych w hash i pobieranie byłoby nieznacznie szybciej.

W każdym razie różnica prawdopodobnie nie będzie ogromna w żaden sposób. Z pewnością rozwiązanie hashtable jest czystsze, więc polecam.

Najlepszym sposobem, aby wiedzieć na pewno, jest napisanie obu wersji i przetestowanie ich za pomocą dużej ilości danych i zmierzenie ich wydajności.

1

Słownik jest lepszy. Słownik powinien być wspierany przez drzewo/hashmap i ma lepszą złożoność czasu niż instrukcja if-else (która jest z grubsza liniowa). Nawet jeśli rzeczywisty czas działania nie jest lepszy, kod będzie czystszy ze słownikiem.

1

Słowniki są jedną z silnie dostrojonych części Pythona. Wydają bardziej czytelny kod. Powinny działać lepiej niż pętle if. Jednak biorąc pod uwagę wkładkę i inne koszty ogólne sugerowałbym, aby użyć modułu timeit i sprawdzić wydajność.

6

Słownik będzie szybszy: chodzi o około O (1), podczas gdy łańcuch instrukcji if jest O (n). Aby wykazać, ten skrypt (make_test.py) Wyjście wola skrypt Pythona, który uruchamia kilka testów peformance:

ifs = [] 
dict = [] 
for i in range(60): 
    string = 'str%d' % i 
    ifs.append(' %sif str == "%s": return %d' % ('el' if i else '', string, i)) 
    dict.append('"%s": %d' % (string, i)) 

print 'dict = {', ','.join(dict), '}' 
print 'def with_dict(str):' 
print ' return dict[str]' 

print 'def with_if(str):' 
print '\n'.join(ifs) 

print ''' 
import timeit 

def test_dict(): 
    for i in range(60): 
     with_dict("str%d" % i) 

def test_if(): 
    for i in range(60): 
     with_if("str%d" %i) 

print 'dict:', timeit.timeit(test_dict, number=10000) 
print 'if: ', timeit.timeit(test_if, number=10000)''' 

uruchomienie go jak python make_test.py | python, daje mi:

dict: 0.706176042557 
if: 1.67383503914 

Oznacza to, że wersja if jest więcej niż 2 razy wolniej niż wersja dict.

+3

Nie zgaduj, zmierz! +1 za Twoje wyniki. :-) – Paddy3118

+0

Twoje porównanie pomija fakt, że jest czas na zbudowanie słownika, który nie jest bez znaczenia, aw wielu przypadkach nie jest buforowany, gdy jest używany jako zamiennik dla instrukcji if. – Silfheed

+0

Może być użyteczne dla kogoś: użyj odwzorowania, jeśli testowany warunek jest niezmienny/stały (użyj ciągu do przechowywania kodu dla późniejszej eval()/exec()), w przeciwnym razie użyj jeśli-else. Użyj globalnego zakresu, zamknięcia lub argumentu ze słowem kluczowym (szybki hack z luką w zabezpieczeniach) do mapowania pamięci podręcznej. – 8day

0

Średnia złożoność czasu dla dict lookup to 0 (1). Najgorszy przypadek to O (n). Istnieją optymalizacje dla słowników za pomocą tylko klawiszy str (twój przypadek użycia).

Zakładając, że kolejność badań w drabinie if/else nie może być zoptymalizowana na podstawie częstotliwości wejścia (np. 60 możliwości, z których 2 występują w 95% przypadków), złożoność serii jeśli instrukcje else są O (n).

Słownik zapewni lepszą wydajność i lepszą czytelność kodu.

0

Które każde rozwiązanie wygląda bardziej elegancko i jest łatwiejsze w utrzymaniu.

W przypadku większego programowania aplikacji ważniejsze jest zoptymalizowanie czasu pracy człowieka i łatwość konserwacji, a następnie optymalizacja czasu komputera. Czas na komputer jest tani. Ludzki czas jest drogi.

Oba rozwiązania mają swoje zalety.

Rozwiązanie if/elif może zapewnić większą elastyczność, jeśli trzeba dodać przepływ sterowania później, np. Zagnieżdżone, jeśli/else.

Jeśli dane pochodzą bezpośrednio ze źródła danych, takiego jak yaml lub baza danych, jasne jest, że rozwiązanie dla dyktatora jest bardziej eleganckie.

Powiązane problemy