tags:

views:

174

answers:

9

Is it possible to take the sum of non-integers in python?

The command

sum([[1],[2]])

for example, gives the error

Traceback (most recent call last):
  File "<pyshell#28>", line 1, in <module>
    sum([[1,2,3],[2,3,4]])
TypeError: unsupported operand type(s) for +: 'int' and 'list'

I suspect sum tries to add 0 to the list [1], resulting in failure. I'm sure there are many hacks to work around this limitation (wrapping stuff in a class, and implementing __radd__ manually), but is there a more elegant way to do this?

A: 

I'm new in python but I think it's impossible unless you will convert string to integer like this

int(myString) 

then it must be ok

holms
Nope, not impossible at all, just need to specify [] as the optional starting value.
Paul McGuire
+8  A: 

It looks like you want this:

>>> sum([[1],[2]], [])
[1, 2]

You're right that it's trying to add 0 to [1] and getting an error. The solution is to give sum an extra parameter giving the start value, which for you would be the empty list.

Edit: As gnibbler says, though, sum is not a good way to concatenate things. And if you just want to aggregate a sequence of things, you should probably use reduce rather than make your own __radd__ function just to use sum. Here's an example (with the same poor behavior as sum):

>>> reduce(lambda x, y: x+y, [[1],[2]])
[1, 2]
Gabe
+7  A: 

It's a bad idea to use sum() on anything other than numbers, as it has quadradic performance for sequences/strings/etc.

Better to use a list comprehension to sum your lists

[j for i in [[1],[2]] for j in i]
gnibbler
+1 not the answer he was looking for, but it's a valueable advice. For linked lists, it would work out fine performance-wise, but those are rare in Python.
delnan
@delnan Why in Python list this would not performance wise?
Xavier Combelle
@Xavier: Python's `list` is not a linked list.
KennyTM
Exactly, it's a plain array. And concatenating two arrays means copying every single elements of one the the end of the other one.
delnan
@delnan, except that `sum()` doesn't do that, it creates a brand new `list` that is the concatenation of the two each iteration, hence the quadratic behaviour
gnibbler
+3  A: 

As the docs say,

The iterable‘s items are normally numbers, and are not allowed to be strings.

What this means is that the tedious process of actually forbidding anything but numbers (except for forbidding summing strings, a particularly heinous and common error) was eschewed -- if you're summing anything but numbers, you'll probably destroy your program's performance for no good purpose, but, hey, Python's not really about stopping programmers from doing every kind of terrible mistake.

If you do insist on doing things the wrong way, as other answers have mentioned, using sum's third parameter (as the starting value, instead of the default, 0) is the right way to do the wrong thing;-). So, the literal answer to your question:

Is it possible to take the sum of non-integers in python?

(once the very erroneous suggestion is removed, that integers behave any differently than any other kind of numbers here, by rephrasing it as "non-numbers" -- summing any kind of numbers is quite fine, and does not necessarily require any special precaution, though math.fsum is better for summing floats) is...: "yes, it is possible (just like it's possible to use a hammer to bang your thumb quite painfully) -- mind you, it's absolutely not advisable (just as hammering your thumb isn't), but, it's definitely possible, if you really insist";-).

Alex Martelli
A: 

Are you asking why [1]+[2] is not the same as sum([[1],[2]])?

I guess it's because "+" is a union operator when applied to lists, rather than an algebraic operator (operator.add) when applied to integers; gnibbler and Gabe's answers are generic unions of lists contained in a list.

But you could be asking something else, apologies if I am missing something.

Marshall Ward
A: 

sum(iterable[, start]) Sums start and the items of an iterable from left to right and returns the total. start defaults to 0. The iterable‘s items are normally numbers, and are not allowed to be strings. In [26]: sum([[1],[2]], []) Out[26]: [1, 2]

As in the Docs...

By the way Gabe has given the right solution to use reduce(lambda x, y: x+y, [[1],[2]]). Alternatively you can use a lay man method:-

In [69]: l = [[1],[2]]

In [70]: a = str(l[0]).strip('[]')

In [71]: b = str(l[1]).strip('[]')

In [72]: l = [int(a), int(b)]

In [73]: l Out[73]: [1, 2]

Tauquir
+1  A: 

I little misunderstood your question to be of addition and made this solution:

# for me the guestion looks for me to do sum of sum of list
# i would do like this
list_of_numlists=[[1,2,3],[2,3,4]]
print "Input =",list_of_numlists
sum_of_it=sum(sum(x) for x in list_of_numlists)
print "Sum = %i" % sum_of_it
## --> Sum = 15
# second version to understand the request is
sum_of_items=[sum(x) for x in zip(*list_of_numlists)]
print "Sum of each is", sum_of_items
""" Output:
Input = [[1, 2, 3], [2, 3, 4]]
Sum = 15
Sum of each is [3, 5, 7]
"

""

Actually you should not talk about sum but concatenating or joining sequences.

Tony Veijalainen
+3  A: 

It is more efficient to concatenate using itertools.chain.

>>> m = [[i] for i in range(200)]
>>> m
[[0], [1], [2], [3], [4], [5], [6], [7], [8], ...]
>>> from itertools import *
>>> list(chain(*m))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, ...]

Personally, I prefer this over list comprehension as it's hard to remember which for loop comes first. There is even a more efficient variant, list(chain.from_iterable(m)).

Microbenchmark results (with Python 3 using the timeit module. A list size of p x q means m = [list(range(q)) for _ in range(p)]):

list size | chain(*m)  | sum(m,[])     | list comp  | flatten    |
----------+------------+---------------+------------+------------+
    2 x 1 |   1.78 µs  |      0.646 µs |   0.905 µs |    1.49 µs |
   20 x 1 |   4.37 µs  |      7.49  µs |   5.19  µs |    3.59 µs |
  200 x 1 |  26.9  µs  |    134     µs |  40     µs |   24.4  µs |
 2000 x 1 | 233    µs  | 12.2       ms | 360     µs | 203     µs |
----------+------------+---------------+------------+------------+
 2 x    1 |   1.78 µs  |      0.646 µs |   0.905 µs |    1.49 µs |
 2 x   10 |   2.55 µs  |      0.899 µs |   3.14  µs |    2.2  µs |
 2 x  100 |   9.07 µs  |      2.03  µs |  17.2   µs |    8.55 µs |
 2 x 1000 |  51.3  µs  |     21.9   µs | 139     µs |   49.5  µs |
----------+------------+---------------+------------+------------+

chain(*m) -> list(chain(*m))
sum(m,[]) -> sum(m, [])
list comp -> [j for i in m for j in i]
flatten   -> icfi = chain.from_iterable; list(icfi(m))

It shows that sum is efficient only when the outer list size is very short. But then you have an even more efficient variant: m[0]+m[1].

KennyTM
Of course you remember first to make it work, then optimize hot spots. Natural and simple expression is more important (readability of code should be preferred). For me [j for i in m for j in i]is self documenting code.
Tony Veijalainen
@Tony: Well, for me `[j for i in m for j in i]` is a [two-level list comprehension](http://google-styleguide.googlecode.com/svn/trunk/pyguide.html#List_Comprehensions).
KennyTM
A: 

For case of one element sequences there is also special solution:

m = [[i] for i in range(200)]
list_of_m = list((zip(*m))[0])
print list_of_m

Also if you have strings in list you can use the standard Python join to catenate

''.join(m)
Tony Veijalainen