The sum builtin accepts an optional start parameter, which defaults to zero. If you have an additive identtity for your class (a class-specific zero of some sort), you can pass that as the second argument:
sum(l_obj, start=Thing())
As a side note, names like l_obj don’t really carry a lot of information; every value in Python is an object of some sort, and all it’s saying is that this is some sort of list. You might not need any of that though; if the “get object here” part is reasonably compact, you could do the whole job in a list comprehension. Here’s an example using simple arithmetic:
def get_total():
l_obj = []
for iobj in range(10):
obj = iobj * iobj
l_obj.append(obj)
tot = sum(l_obj)
return tot
This could be implemented much more simply like this:
def get_total():
return sum([i * i for i in range(10)])
Thank you for your answer. Yes, that’s right, the start is allowed, but the point is that the value is zero by default. In general, obj + 0 does not make sense unless you define __add__ in such a way that it just returns obj. Your suggestion of implementing an additive identity is interesting and could be done. However, wouldn’t it be easier to make 0 an additive identity with?
def __add__(self, obj):
if obj == 0:
return self
#something here that adds obj to self and creates val, an instance of the
#current class.
return val
It’s not rethoric though, I wonder if this is the best approach or if there is a good reason why implementing an additive (and in the long term multiplicative, etc) identity is a better idea. I just do not want to be stuck with a pile of badly designed code that I will have to rewrite at some point
Yes, in my code I do not use l_obj but something more meaningful, here I wanted to be generic so I used obj.
I have a collection of objects for which the sum operation makes sense.
Therefore I do:
def __add__(self, obj):
#something here that adds obj to self and creates val, an instance of the
#current class.
return val
Note that that only makes sense inside a class definition. Eg:
class C:
def __add__(self, obj):
#something here that adds obj to self and creates val, an
nstance of the
#current class.
return val
Then if you make objects of type C (which isn’t complete yet) then you
can add them together.
however I have a piece of code that looks like:
def get_total():
l_obj = []
for iobj in range(10):
#get object here
l_obj.append(obj)
tot = sum(l_obj)
return tot
Here sum adds to zero, the number.
The builtin sum adds everything in its argument to 0 (or some other
starting value if you like). So, yes.
Thus, I should do something like:
def __add__(self, obj):
if obj == 0:
return self
#something here that adds obj to self and creates val, an instance of the
#current class.
return val
This isn’t very different. All it has is a tiny optimisation to return self if adding obj to it is like adding 0 to it i.e. do not return
a new object if that is the case.
But this assumes a fair bit about your class. You might be thinking in
terms of natural numbers, but maybe your objects are something else? You
haven’t said yet.
if you’re entirely new to implementing classes with operations like __add__, I’d start with objects which just store a number because it
makes the addition easy to implement.
this should work, but is this recommended/sensible? I do want to use sum to avoid messy code.
Well, if you do it right, yes. sum() starts with a starting value, and
adds everything you give it to that. So the starting value wants an add
operation which works with the objects.
Let’s see what __add__ does, here:
Ignore the preview (which shows the top of the page), the link itself
goes directly to the __add__ method definition.
If you’ve got x+y, Python runs type(x).__add__(x,y). This means that
we’re calling x’s add operation to add x and y.
In order to use your__add__ operation, the left hand object needs
to be of your type. If you’ve got some class C and you’re using sum(), that means you need to invoke sum() like this:
sum(your_objects, start=c0)
which c0 is whatever the sum should start with. Maybe you can make a
“zero” object, eg:
c0 = C(0)
if that makes sense for your objects, and use that.
Without the start=, the sum() function starts with 0, and uses the int.__add__ operation to add up the objects.
Put some print() calls inside your __add__ method to see what
arguments it is getting, and to see if it is even being called
Because I was trying to keep it as close to the original as possible. Plus, if you add the start parameter, the benefit largely vanishes, as the genexp needs to be parenthesized.
Defining __add__ for the input 0, has as a consequence that now the arithmetic of your class has 0 as a valid operand. That means that the addition of 0 in sum works, but it would also work any unintended addition of 0 somewhere else. If addition of 0 makes sense in your case, in general, then that is fine. If not, it would be better that unintended occurrences of + 0 keep being an error.
You could define an instance of your class that is an additive neutral, but it is not necessary. You don’t necessarily need to define the empty sum. That is entirely up to you and your specific application. You could use the first element of the list as the value of start and the rest to be the first argument of sum.