Czy istnieje bardziej zwięzły, skuteczny lub po prostu pytoniczny sposób na wykonanie następujących czynności?
def product(list):
p = 1
for i in list:
p *= i
return p
EDYTOWAĆ:
Właściwie uważam, że jest to nieznacznie szybsze niż użycie operator.mul:
from operator import mul
# from functools import reduce # python3 compatibility
def with_lambda(list):
reduce(lambda x, y: x * y, list)
def without_lambda(list):
reduce(mul, list)
def forloop(list):
r = 1
for x in list:
r *= x
return r
import timeit
a = range(50)
b = range(1,50)#no zero
t = timeit.Timer("with_lambda(a)", "from __main__ import with_lambda,a")
print("with lambda:", t.timeit())
t = timeit.Timer("without_lambda(a)", "from __main__ import without_lambda,a")
print("without lambda:", t.timeit())
t = timeit.Timer("forloop(a)", "from __main__ import forloop,a")
print("for loop:", t.timeit())
t = timeit.Timer("with_lambda(b)", "from __main__ import with_lambda,b")
print("with lambda (no 0):", t.timeit())
t = timeit.Timer("without_lambda(b)", "from __main__ import without_lambda,b")
print("without lambda (no 0):", t.timeit())
t = timeit.Timer("forloop(b)", "from __main__ import forloop,b")
print("for loop (no 0):", t.timeit())
daje mi
('with lambda:', 17.755449056625366)
('without lambda:', 8.2084708213806152)
('for loop:', 7.4836349487304688)
('with lambda (no 0):', 22.570688009262085)
('without lambda (no 0):', 12.472226858139038)
('for loop (no 0):', 11.04065990447998)
reduce
odpowiedzi podnoszą aTypeError
, podczas gdyfor
odpowiedź w pętli zwraca 1. Jest to błąd wfor
odpowiedzi pętli (iloczyn pustej listy jest nie większy niż 17 lub „pancernik”).list
jako nazwy zmiennej ...+
dla tego typu listy (podobnie dla produktu /*
). Teraz zdaję sobie sprawę, że Python jest typowany dynamicznie, co utrudnia sprawę, ale jest to problem rozwiązany w rozsądnych językach ze statycznymi systemami typów, takimi jak Haskell. Ale i takPython
pozwala tylkosum
pracować na liczbach, ponieważsum(['a', 'b'])
nawet nie działa, więc znowu mówię, że0
ma to sens dla produktusum
i1
dla produktu.Odpowiedzi:
Bez użycia lambda:
jest lepiej i szybciej. W Pythonie 2.7.5
W następującej konfiguracji:
Wyniki w Pythonie 2.7.5
Wynik:
np.prod
jest najszybszy, jeśli używasznp.array
jako struktury danych (18x dla małej tablicy, 250x dla dużej tablicy)z pythonem 3.3.2:
Czy Python 3 jest wolniejszy?
źródło
int
to Python 2long
. Python 2 będzie używał „int”, dopóki nie przekroczy 32 bitów; Python 3 będzie używać „long” od początku. (2) Python 3.0 był „dowodem słuszności koncepcji”. Zaktualizuj do wersji 3.1 JAK NAJSZYBCIEJ!reduce
operatora zfunctools
modułu w Pythonie 3. IEfrom functools import reduce
.źródło
operator.mul
lepszego sposobu na zrobienie tego.reduce
)from functools import reduce
aby działał w Pythonie 3jeśli masz tylko numery na swojej liście:
EDYCJA : jak wskazano w @ off99555, nie działa to w przypadku dużych wyników całkowitych, w którym to przypadku zwraca wynik typu,
numpy.int64
podczas gdy rozwiązanie Iana Clellanda jest oparte na dużych wynikach całkowitychoperator.mul
ireduce
działa z nimi, ponieważ zwracalong
.źródło
from numpy import prod; prod(list(range(5,101)))
i wyszło0
, czy możesz odtworzyć ten wynik w Pythonie 3?prod
zwraca wynik typunumpy.int64
i otrzymujesz przepełnienie (w rzeczywistości wartość ujemną) już dlarange(5,23)
. Użyj rozwiązania @Ian Clelland opartego naoperator.mul
ireduce
dla dużych liczb całkowitych (zwracalong
w tym przypadku a, co wydaje się mieć dowolną precyzję).np.prod(np.arange(5.0,101.0))
albo przekonwertuj ją na typ pływający wykonującnp.prod(np.array(range(5,101)).astype(np.float64))
. Zauważ, że NumPy używanp.float64
zamiastfloat
. Nie znam różnicy.Cóż, jeśli naprawdę chcesz zrobić jedną linię bez importowania czegokolwiek, co możesz zrobić:
Ale nie rób tego.
źródło
źródło
functools.reduce(..)
w pythonie3Począwszy od modułu w bibliotece standardowej
Python 3.8
dodanoprod
funkcjęmath
:która zwraca iloczyn
start
wartości (domyślnie: 1) razy iterowalna liczba:Zauważ, że jeśli iterowalna jest pusta, to da
1
(lubstart
wartość, jeśli została podana).źródło
Pamiętam kilka długich dyskusji na temat comp.lang.python (przepraszam, zbyt leniwy, by teraz tworzyć wskaźniki), w wyniku których stwierdzono, że Twoja oryginalna
product()
definicja jest najbardziej Pythonowa .Zwróć uwagę, że propozycja nie polega na pisaniu pętli for za każdym razem, gdy chcesz to zrobić, ale na jednorazowym napisaniu funkcji (według typu redukcji) i wywołaniu jej w razie potrzeby! Wywoływanie funkcji redukcyjnych jest bardzo Pythonowe - działa słodko z wyrażeniami generatora, a od udanego wprowadzenia
sum()
, Python stale rozwija się coraz więcej wbudowanych funkcji redukcyjnych -any()
iall()
są najnowszymi dodatkami ...Ten wniosek jest trochę oficjalny -
reduce()
został usunięty z wbudowanych w Pythonie 3.0, mówiąc:Zobacz też The fate of reduction () in Python 3000, aby uzyskać pomocniczy cytat z Guido (i kilka mniej wspierających komentarzy Lispersów, którzy czytali ten blog).
PS, jeśli przypadkiem potrzebujesz
product()
kombinatoryki, zobaczmath.factorial()
(nowy 2.6).źródło
Celem tej odpowiedzi jest przedstawienie obliczeń przydatnych w pewnych okolicznościach - a mianowicie, gdy a) mnożonych jest duża liczba wartości, tak że produkt końcowy może być bardzo duży lub bardzo mały, oraz b) nie należy Bardzo zależy mi na dokładnej odpowiedzi, ale zamiast tego mam kilka sekwencji i chcę mieć możliwość zamówienia ich na podstawie każdego produktu.
Jeśli chcesz pomnożyć elementy listy, gdzie l jest listą, możesz:
To podejście nie jest tak czytelne jak
Jeśli jesteś matematykiem, który nie jest zaznajomiony z redukcją (), może być odwrotnie, ale nie radziłbym używać go w normalnych okolicznościach. Jest też mniej czytelny niż wspomniana w pytaniu funkcja product () (przynajmniej dla nie-matematyków).
Jeśli jednak kiedykolwiek znajdziesz się w sytuacji, w której ryzykujesz niedomiar lub przepełnienie, na przykład w
a twoim celem jest porównanie produktów o różnych sekwencjach, a nie wiedzieć, jakie to produkty
jest drogą do zrobienia, ponieważ praktycznie niemożliwe jest wystąpienie problemu w świecie rzeczywistym, w którym można by przepełnić lub niedopełnić przy takim podejściu. (Im większy jest wynik tego obliczenia, tym większy byłby iloczyn, gdybyś mógł go obliczyć).
źródło
Przetestowałem różne rozwiązania za pomocą perfplot ( mój mały projekt) i znalazłem to
jest zdecydowanie najszybszym rozwiązaniem (jeśli lista nie jest zbyt krótka).
Kod do odtworzenia fabuły:
źródło
Jestem zaskoczony, że nikt nie zasugerował używania
itertools.accumulate
zoperator.mul
. Pozwala to uniknąć używaniareduce
, które jest inne dla Pythona 2 i 3 (ze względu nafunctools
import wymagany dla Pythona 3), a ponadto sam Guido van Rossum uważa, że nie jest w Pythonie :Przykład:
źródło
Jedną z opcji jest użycie
numba
i@jit
lub@njit
dekorator . Dokonałem również jednej lub dwóch drobnych poprawek w Twoim kodzie (przynajmniej w Pythonie 3 „lista” to słowo kluczowe, którego nie powinno się używać w nazwie zmiennej):Ze względów czasowych musisz uruchomić raz, aby najpierw skompilować funkcję przy użyciu numba. Ogólnie rzecz biorąc, funkcja zostanie skompilowana przy pierwszym wywołaniu, a następnie wywołana z pamięci (szybciej).
Teraz, kiedy wykonasz swój kod, będzie on działał ze skompilowaną wersją funkcji. Zmierzyłem czas za pomocą notatnika Jupyter i
%timeit
magicznej funkcji:Zauważ, że na moim komputerze, na którym działa Python 3.5, natywna
for
pętla Pythona była w rzeczywistości najszybsza. Może być tu pewien podstęp, jeśli chodzi o mierzenie wydajności ozdobionej numba za pomocą notebooków Jupyter i%timeit
magiczną funkcją. Nie jestem pewien, czy powyższe czasy są prawidłowe, więc polecam wypróbowanie tego w systemie i sprawdzenie, czy numba zapewnia wzrost wydajności.źródło
Najszybszy sposób, jaki znalazłem, to użycie while:
a czasy to:
źródło
Wynik w Pythonie 3 dla testów OP: (najlepiej 3 dla każdego)
źródło
Działa to również w przypadku oszustwa
źródło
print
zwrotem. Ponadto nie ma potrzeby przechowywania wartości pośrednich na liście, wystarczy przechowywaćp
między iteracjami.