261

Python's sum() function returns the sum of numbers in an iterable.

sum([3,4,5]) == 3 + 4 + 5 == 12

I'm looking for the function that returns the product instead.

somelib.somefunc([3,4,5]) == 3 * 4 * 5 == 60

I'm pretty sure such a function exists, but I can't find it.

Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
Patrick McElhaney
  • 57,901
  • 40
  • 134
  • 167

9 Answers9

235

Historically, Guido vetoed the idea: http://bugs.python.org/issue1093

As noted in that issue, you can make your own:

from functools import reduce # Valid in Python 2.6+, required in Python 3
import operator

reduce(operator.mul, (3, 4, 5), 1)
ojrac
  • 13,231
  • 6
  • 37
  • 39
  • 7
    Here is a great example of where there is a "need for this," to quote Guido: product(filter(None, [1,2,3,None])). Hopefully it will be included someday. – the911s Mar 05 '14 at 21:30
  • 22
    Isn't Guido also the guy who doesn't like `reduce`? – Chris Martin Oct 02 '15 at 05:48
  • 3
    Yep -- and reduce is no longer even a builtin in Python 3. IMO, we don't need every possible list operator added to the global builtins when a standard (or 3rd party) library would do. The more builtins you have, the more common words become off-limits as local variable names. – ojrac Oct 13 '15 at 19:56
  • 13
    Just found this nugget in [Guido's blog post about reduce()](http://www.artima.com/weblogs/viewpost.jsp?thread=98196). **"We already have sum(); I'd happily trade reduce() for product()..."**. If anyone wants to petition for including `product()` in the standard library, the number of views on this question may help make the case. – Patrick McElhaney Jul 18 '16 at 17:02
  • 2
    @PatrickMcElhaney It sounds like python3 already got rid of the reduce builtin. I think product missed its chance. ;) – ojrac Jul 26 '16 at 14:27
  • 1
    @the911s I don't get it, what's wrong with `import operator; print(reduce(operator.mul,filter(None, [1,2,3,None])))`? – user202729 Aug 26 '18 at 04:50
  • @user202729 `[0, 1, 2, 3]` now results in `6` instead of `0` – vallentin Oct 27 '18 at 09:54
  • 2
    So how come sum() got a free pass then? – RCross Feb 06 '20 at 16:07
  • 1
    I wish he would've explained his "dissuasion". – young_souvlaki Jan 16 '22 at 18:46
  • An obvious case of not foreseeing things like Sagemath and the routine need to compute products (as well as sums) of thousands of elements in various algebras in which this is entirely feasible and does not cause the representation to explode. – Szczepan Hołyszewski Feb 12 '23 at 15:27
143

Update:

In Python 3.8, the prod function was added to the math module. See: math.prod().

Older info: Python 3.7 and prior

The function you're looking for would be called prod() or product() but Python doesn't have that function. So, you need to write your own (which is easy).

Pronouncement on prod()

Yes, that's right. Guido rejected the idea for a built-in prod() function because he thought it was rarely needed.

Alternative with reduce()

As you suggested, it is not hard to make your own using reduce() and operator.mul():

from functools import reduce  # Required in Python 3
import operator
def prod(iterable):
    return reduce(operator.mul, iterable, 1)

>>> prod(range(1, 5))
24

Note, in Python 3, the reduce() function was moved to the functools module.

Specific case: Factorials

As a side note, the primary motivating use case for prod() is to compute factorials. We already have support for that in the math module:

>>> import math

>>> math.factorial(10)
3628800

Alternative with logarithms

If your data consists of floats, you can compute a product using sum() with exponents and logarithms:

>>> from math import log, exp

>>> data = [1.2, 1.5, 2.5, 0.9, 14.2, 3.8]
>>> exp(sum(map(log, data)))
218.53799999999993

>>> 1.2 * 1.5 * 2.5 * 0.9 * 14.2 * 3.8
218.53799999999998

Note, the use of log() requires that all the inputs are positive.

erb
  • 14,503
  • 5
  • 30
  • 38
Raymond Hettinger
  • 216,523
  • 63
  • 388
  • 485
  • 2
    You might want to add that the floats in the last example need to be _positive_. Otherwise, you might have to use cmath, but even then it won't really work in all cases. – Veky Feb 15 '19 at 06:07
48

There's a prod() in numpy that does what you're asking for.

istruble
  • 13,363
  • 2
  • 47
  • 52
Benjamin
  • 489
  • 4
  • 2
  • 4
    note: doesn't support Python longs (arbitrary precision integers) so `np.prod(range(1,13))` gives the correct answer equal to 12! but `np.prod(range(1,14))` does not. – Jason S Nov 13 '15 at 17:38
  • 2
    @JasonS `np.prod(arange(1,14, dtype='object'))`? – endolith Apr 26 '16 at 04:09
  • 1
    The [`math.prod()`](https://docs.python.org/3.8/library/math.html#math.prod) function will make this answer obsolete. – Benoît P Feb 26 '19 at 16:11
  • 1
    Still tedious to have to import math when you want to do this in a simple one-liner. I miss reduce() and the Guido-rejected product(). – RCross Feb 06 '20 at 16:11
43

There isn't one built in, but it's simple to roll your own, as demonstrated here:

import operator
def prod(factors):
    return reduce(operator.mul, factors, 1)

See answers to this question:

Which Python module is suitable for data manipulation in a list?

Community
  • 1
  • 1
zweiterlinde
  • 14,557
  • 2
  • 27
  • 32
  • 9
    If using Python 3 use `functools.reduce` instead of `reduce`. – Steven Rumbalski Oct 29 '15 at 17:25
  • 3
    For even more functools fun: `prod = functools.partial(functools.reduce, operator.mul)` – bukzor Dec 29 '18 at 01:47
  • So in Python 3 I need *two* imports to do something so basic?! – A. Donda Jul 18 '21 at 09:54
  • @A.Donda You need to use imports in Python to do far more basic things: the square root function is in Math, Threads are in threading, etc etc. Python doesn't eschew namespaces, it's actually an explicit part of the Zen of Python that it embraces them. – Marcel Besixdouze Dec 09 '21 at 22:36
  • @MarcelBesixdouze, yes, I agree that namespaces are one honking great idea. But imho in a language that has native lists, multiplying a bunch of numbers should be a builtin. And I consider it to be more basic than square roots and threading. In particular the latter is complex enough to warrant a module. – A. Donda Dec 18 '21 at 17:59
  • @A.Donda Python's "list" is a dynamic array, not a list. But given that python lists and iterators are so important, I would expect 'product' to do a cartesian product of iterables, if anything. Other languages where it's easy to do this usually just have variadic "add" and "times" functions that take any number of arguments, rather than actually operating on lists. – Marcel Besixdouze Dec 19 '21 at 00:30
  • @MarcelBesixdouze, yes, just like 'sum' does a cartesian sum of iterables. – A. Donda Dec 20 '21 at 22:09
  • @A.Donda I'm not aware of that. `sum` calls +, and there is no + defined for general iterators. If there were, it should be chaining (itertools.chain), just like if * were defined on iterators it should do product (itertools.product). – Marcel Besixdouze Dec 21 '21 at 01:16
29
Numeric.product 

( or

reduce(lambda x,y:x*y,[3,4,5])

)

Steve B.
  • 55,454
  • 12
  • 93
  • 132
24

Use this

def prod(iterable):
    p = 1
    for n in iterable:
        p *= n
    return p

Since there's no built-in prod function.

wim
  • 338,267
  • 99
  • 616
  • 750
S.Lott
  • 384,516
  • 81
  • 508
  • 779
8

Perhaps not a "builtin", but I consider it builtin. anyways just use numpy

import numpy 
prod_sum = numpy.prod(some_list)
katiex7
  • 863
  • 12
  • 23
  • 3
    That's dangerously close to a "works on my machine" statement! Numpy, lovely though it is, is unequivocaly *not* a builtin. – RCross Feb 06 '20 at 16:10
4

I prefer the answers a and b above using functools.reduce() and the answer using numpy.prod(), but here is yet another solution using itertools.accumulate():

import itertools
import operator
prod = list(itertools.accumulate((3, 4, 5), operator.mul))[-1]
tommy.carstensen
  • 8,962
  • 15
  • 65
  • 108
1

You can also encode your list of numbers as a pd.Series and then use pd.Series.product():

>>> import pandas as pd
>>> pd.Series([5,3,-1]).product()
-15
hipoglucido
  • 545
  • 1
  • 7
  • 20