Taki kod często się zdarza:
l = []
while foo:
#baz
l.append(bar)
#qux
Jest to bardzo powolne, jeśli masz zamiar dodać tysiące elementów do listy, ponieważ lista będzie musiała być stale zmieniana, aby dopasować do nowych elementów.
W Javie możesz utworzyć ArrayList o początkowej pojemności. Jeśli masz pojęcie, jak duża będzie Twoja lista, będzie to o wiele bardziej wydajne.
Rozumiem, że taki kod często można przeformułować na listę. Jeśli pętla for / while jest bardzo skomplikowana, jest to niewykonalne. Czy jest jakiś odpowiednik dla nas programistów Python?
python
list
dictionary
initialization
Claudiu
źródło
źródło
Odpowiedzi:
Wyniki . (oceniaj każdą funkcję 144 razy i średni czas trwania)
Wnioski . To nie ma znaczenia.
Przedwczesna optymalizacja jest źródłem wszelkiego zła.
źródło
Listy w języku Python nie mają wbudowanej wstępnej alokacji. Jeśli naprawdę musisz utworzyć listę i uniknąć nakładania się na dołączanie (i powinieneś to zrobić), możesz to zrobić:
Być może możesz uniknąć listy, używając zamiast tego generatora:
W ten sposób lista nie zawsze jest przechowywana w całości w pamięci, a jedynie generowana w razie potrzeby.
źródło
Wersja skrócona: użyj
aby wstępnie przydzielić listę (tzn. móc adresować elementy listy o rozmiarze zamiast stopniowo tworzyć listę przez dołączanie). Ta operacja jest BARDZO szybka, nawet na dużych listach. Przydzielanie nowych obiektów, które zostaną później przypisane do elementów listy, zajmie DUŻO więcej i będzie wąskim gardłem w twoim programie, pod względem wydajności.
Długa wersja:
Myślę, że należy wziąć pod uwagę czas inicjalizacji. Ponieważ w Pythonie wszystko jest referencją, nie ma znaczenia, czy ustawisz każdy element na None, czy jakiś ciąg znaków - w każdym razie jest to tylko referencja. Chociaż zajmie to więcej czasu, jeśli chcesz utworzyć nowy obiekt dla każdego elementu, do którego ma się odwoływać.
W przypadku Python 3.2:
Ocena:
Jak widać, utworzenie dużej listy odniesień do tego samego obiektu None zajmuje bardzo mało czasu.
Przygotowywanie lub przedłużanie trwa dłużej (nic nie oceniałem, ale po kilkukrotnym uruchomieniu mogę powiedzieć, że rozszerzanie i dodawanie zajmuje mniej więcej ten sam czas).
Przydzielanie nowego obiektu dla każdego elementu - to zajmuje najwięcej czasu. I odpowiedź S.Lott tak robi - za każdym razem formatuje nowy ciąg. Co nie jest bezwzględnie wymagane - jeśli chcesz wstępnie przydzielić trochę miejsca, po prostu utwórz listę Brak, a następnie przypisz dane do elementów listy do woli. Tak czy inaczej, generowanie danych zajmuje więcej czasu niż dodanie / rozszerzenie listy, niezależnie od tego, czy generujesz ją podczas tworzenia listy, czy później. Ale jeśli chcesz mieć słabo zaludnioną listę, rozpoczęcie od listy Brak jest zdecydowanie szybsze.
źródło
[]*
podejściePythonicznym sposobem na to jest:
lub jakąkolwiek wartością domyślną, którą chcesz zastosować, np
[Edycja: Zastrzeżenie strzeże
[Beer()] * 99
składni tworzy jednąBeer
, a następnie wypełnia się tablicę 99 odniesienia do tego samego pojedynczego przykład]Domyślne podejście Pythona może być dość wydajne, chociaż wydajność maleje wraz ze wzrostem liczby elementów.
Porównać
z
W moim systemie Windows 7 i7 64-bitowy Python daje
Podczas gdy C ++ daje (zbudowany z MSVC, 64-bit, włączone optymalizacje)
Kompilacja debugowania w C ++ daje:
Chodzi o to, że dzięki Python możesz osiągnąć 7-8% poprawę wydajności, a jeśli myślisz, że piszesz aplikację o wysokiej wydajności (lub jeśli piszesz coś, co jest używane w serwisie internetowym lub coś takiego), to nie można tego wąchać, ale może trzeba przemyśleć swój wybór języka.
Poza tym kod Pythona nie jest tak naprawdę kodem Pythona. Przejście na kod naprawdę Pythonesque zapewnia lepszą wydajność:
Co daje
(w 32-bitowym programie doGenerator działa lepiej niż doAllocate).
Tutaj różnica między doAppend i doAllocate jest znacznie większa.
Oczywiście różnice tutaj obowiązują tylko wtedy, gdy robisz to więcej niż kilka razy lub jeśli robisz to w mocno obciążonym systemie, w którym liczby te zostaną skalowane o rzędy wielkości, lub jeśli masz do czynienia z znacznie większe listy.
Chodzi o to: zrób to pythonowy sposób, aby uzyskać najlepszą wydajność.
Ale jeśli martwisz się ogólną wydajnością na wysokim poziomie, Python jest niewłaściwym językiem. Najbardziej podstawowym problemem jest to, że wywołania funkcji Pythona tradycyjnie były do 300 razy wolniejsze niż w innych językach ze względu na funkcje Pythona, takie jak dekoratory itp. ( Https://wiki.python.org/moin/PythonSpeed/PerformanceTips#Data_Aggregation#Data_Aggregation ).
źródło
timeit
timeit
, którego powinieneś używać, mierząc czas w swoim kodzie Pythona; Oczywiście nie mówię o C ++.bottles = [Beer()] * 99
nie tworzy 99 obiektów piwa. Zamiast tego tworzy jeden obiekt Beer z 99 referencjami do niego. Jeśli go zmutujesz, wszystkie elementy na liście zostaną zmutowane, ponieważ(bottles[i] is bootles[j]) == True
każdyi != j. 0<= i, j <= 99
.Jak wspomnieli inni, najprostszy sposób na wstępne zaszczepienie listy
NoneType
obiektami.To powiedziawszy, powinieneś zrozumieć sposób, w jaki faktycznie działają listy Python, zanim zdecydujesz, że jest to konieczne. W implementacji listy CPython podstawowa tablica jest zawsze tworzona z wykorzystaniem przestrzeni nad głową, w stopniowo rosnących rozmiarach
( 4, 8, 16, 25, 35, 46, 58, 72, 88, 106, 126, 148, 173, 201, 233, 269, 309, 354, 405, 462, 526, 598, 679, 771, 874, 990, 1120, etc)
, tak że zmiana rozmiaru listy nie zdarza się prawie tak często.Z powodu tego zachowania większość
list.append()
funkcji jestO(1)
złożonością dopisów, posiadając tylko większą złożoność przy przekraczaniu jednej z tych granic, w którym to momencie złożoność będzieO(n)
. Takie zachowanie prowadzi do minimalnego wydłużenia czasu wykonania w odpowiedzi S. Lott.Źródło: http://www.laurentluce.com/posts/python-list-implementation/
źródło
Uruchomiłem kod @ s.lott i wygenerowałem ten sam 10% wzrost perf przez wstępne przydzielenie. Wypróbowałem pomysł @ Jeremy'ego za pomocą generatora i byłem w stanie zobaczyć perf gen gen lepiej niż doAllocate. Dla mojego pro 10% poprawa ma znaczenie, więc dziękuję wszystkim, ponieważ pomaga to wielu.
źródło
Obawy dotyczące wstępnej alokacji w Pythonie powstają, jeśli pracujesz z Numpy, która ma więcej tablic podobnych do C. W tym przypadku obawy przed alokacją dotyczą kształtu danych i wartości domyślnej.
Rozważ numpy, jeśli wykonujesz obliczenia numeryczne na ogromnych listach i chcesz wydajności.
źródło
W przypadku niektórych aplikacji słownik może być tym, czego szukasz. Na przykład w metodzie find_totient wygodniej było używać słownika, ponieważ nie miałem indeksu zerowego.
Ten problem można również rozwiązać za pomocą wstępnie przydzielonej listy:
Wydaje mi się, że nie jest to tak eleganckie i podatne na błędy, ponieważ przechowuję Żadne, które mogłyby rzucić wyjątek, jeśli przypadkowo użyję ich źle, a także dlatego, że muszę pomyśleć o skrajnych przypadkach, których mapa pozwala mi uniknąć.
To prawda, że słownik nie będzie tak wydajny, ale jak zauważyli inni, niewielkie różnice w prędkości nie zawsze są warte znacznych zagrożeń związanych z konserwacją.
źródło
Z tego, co rozumiem, listy python są już dość podobne do ArrayLists. Ale jeśli chcesz poprawić te parametry, znalazłem ten post w sieci, który może być interesujący (po prostu stwórz własne
ScalableList
rozszerzenie):http://mail.python.org/pipermail/python-list/2000-May/035082.html
źródło