Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to implement built-in sum() of the class?

Tags:

python

The built-in function sum() applied to a dictionary returns the sum of its keys:

>>> sum({1: 0, 2: 10})
3

I'd like to create a subclass of the dictionary, say SubDict, and override some function to return the sum of its values, i.e.

>>> sum(SubDict((1, 0), (2, 10))
10

Which function do I need to override to achieve this functionality?

This is a general question, how to implement the built-in sum() function to a class, not just in this particular case.

like image 409
Max Li Avatar asked Dec 25 '22 22:12

Max Li


2 Answers

sum is effectively implemented like this:

def sum(sequence, start=0):
    for value in sequence:
        start = start + value
    return start

So, you can't override sum directly… but if you can override the way for value in … works on your sequence,* or the way + works on your values, that will affect sum automatically. Of course either one will have side effects—you'll be affecting any iteration of your sequence, or any addition of your values, not just the ones inside sum.

To override iteration, you need to provide a __iter__ method that returns an appropriate iterator. To override addition, you need to provide a __add__ method.


But really, why are you trying to "override sum"? Why not just write a new function that does what you want? You can add code that special-cases your type(s) and then falls back to the builtin sum otherwise. If you want to make it more "open", you can use PEP 443 single-dispatch to make it easy to register new types to be special-cased. I think that's what you really want here.


* As agf points out in the comments, despite the parameter being called sequence, it actually takes any iterable. Which is a good thing, because dictionaries aren't sequences…

like image 85
abarnert Avatar answered Jan 05 '23 23:01

abarnert


You can just do:

In [1]: sum({1:0,2:10}.values())
Out[1]: 10

If you want to implement a subclass who's sum will be the sum of values, just override the __iter__ method:

In [21]: class MyDict(dict):
   ....:     def __iter__(self):
   ....:         for value in self.values():
   ....:             yield value
   ....:

In [22]: d = MyDict({1:0,2:10})

In [23]: sum(d)
Out[23]: 10

But then you wont be able to do:

for key in d:
    print d[key]

Because the __iter__ function will return values... You'll always have to use the keys() function:

for key in d.keys():
    print d[key]

Better solution would be to add a sum method:

In [24]: class MyDict(dict):
   ....:     def sum(self):
   ....:         return sum(self.values())
   ....:

In [25]: d = MyDict({1:0,2:10})

In [26]: d.sum()
Out[26]: 10
like image 24
Viktor Kerkez Avatar answered Jan 06 '23 01:01

Viktor Kerkez