1

Why is this code

sum(x for x in range(10))

valid? Why encasing x for x in range(10) in square brackets is not necessary?

  • 2
    See [Generator Expressions vs List Comprehension](https://stackoverflow.com/questions/47789/generator-expressions-vs-list-comprehension) – khelwood Oct 25 '19 at 09:25
  • strictly speaking, neither is necessary here, `sum(range(10))` – Sayse Oct 25 '19 at 09:28
  • @khelwood, so ```sum(x for x in range(10))``` is implicitely converted to ```sum()```? Am I getting it right? Then how reliable is this behavior? – steam_engine Oct 25 '19 at 09:33
  • 1
    Completely reliable. It's part of the language. If you write `( for in )`, you've written a generator expression. – khelwood Oct 25 '19 at 09:35
  • @khelwood, you may be right but I raise the same concern as I did it an answer below. Surely `sum(X)` means `X` given to the `sum` function. If that's the case, `X` is `x for x in range(10)` rather than the generator `(x for x in range(10))`. – paxdiablo Oct 25 '19 at 09:38
  • When you write `(x for x in range(10))` you are writing `(generator expression)`. So `sum` receives the generator expression as its argument. – khelwood Oct 25 '19 at 09:43
  • @khelwood, my question was about omitting round brackets. Apparently it is permitted for the sake of simplicity in case of sole argument (I actually got a special syntax error after trying it with function with multiple argument). – steam_engine Oct 25 '19 at 09:44

2 Answers2

3

I think this has pretty much already been said in comments, but for posterity: Section 6.2.8 of the (Python 3.8.0) docs state that "[t]he parentheses can be omitted on calls with only one argument. See section Calls for details."

Edit: And, for completeness, the "Calls" section goes on to say that the actual parameters of a call can be either a comma-separated list of parameters, or a single comprehension, which is the x for x in ... stuff without brackets.

Ture Pålsson
  • 6,088
  • 2
  • 12
  • 15
1

It creates a generator expression.

>>> nums = (x for x in range(4))
>>> nums
<generator object <genexpr> at 0x000001E52747B2B0>
>>> next(nums)
0
>>> next(nums)
1
>>> next(nums)
2
>>> next(nums)
3
>>> next(nums)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

What is a generator? It basically doesn't execute unless it is asked to execute. A list comprehension will first create a list and then pass that list to the sum function.

Getting rid of the square brackets makes it a generator which will send each generated value to sum without creating the list explicitly. sum will keep asking for numbers from the genexpr and stop when StopIteration is raised. This is faster than using a list comprehension.

If you have a very long sequence, and you use a list comprehension with the sum function, python will first take time to generate the entire list and then sum will again calculate the sum of all the elements. Using a generator expression with sum will reduce a lot of execution time because sum will get each value as soon as it is generated.

Diptangsu Goswami
  • 5,554
  • 3
  • 25
  • 36