How to use a custom function in max(x, key=custom_

2019-06-07 22:58发布

问题:

I have a custom data type, say: mytime, which represent hours and minutes, such as 29:45, it is 29 hours and 45 minutes.

I want to use max built-in function to find the item in a list of lists, whose sum of its elements is the greatest, where all lists contain values of mytime type.

x = [[a, b], [c, d]]

a,b,c,d are of mytime type.

max(x, key=sum) 

won't work here, because a,b,c,d, are not integers.

If I type a + b at python command line, I get the sum of these two time values, result is of mytime type, without any errors.

How do I use max function here?

回答1:

Let's say your class looks like this:

class mytime(object):

    def __init__(self, h, m):
        self.h = h
        self.m = m

    def __add__(self, other):
        return mytime(self.h + other.h, self.m + other.m)

    def __repr__(self):
        return '%i:%i' % (self.h, self.m)

and you use it like this:

a = mytime(10, 10)        
b = mytime(2, 22)
print a + b 

and it will work as expect:

12:32


Problem:

What you want to do is:

l = [a, b]
print sum(l)

but it will fail:

TypeError: unsupported operand type(s) for +: 'int' and 'mytime'

The problem is that the sum function will start with 0 and will add up all values of the list. It will try to evaluate

0 + mytime(10, 10)    

which will fail.


Solution:

The solution to your problem is implementing the __radd__ function, which represents "reverse add" and is called when the arguments can't be resolved in the "forward" direction. For example, x + y is evaluated as x.__add__(y) if possible, but if that doesn't exist then Python tries y.__radd__(x).

So you can add the following method to your class:

def __radd__(self, other):
    return mytime(self.h, self.m)

and the sum function will work for you (in this implementation ignoring the other value, which is probably fine in your case).



回答2:

You can write your own sum function:

def my_sum(item):
    return sum(60 * e[0] + e[1] for e in item)

x = [[(2,0), (3,0)], [(9, 0), (4, 0)]]
print max(x, key=my_sum)

I have represented your mytime data structure as tuples (with hours and minutes) so you may need to adjust my_sum to your data structure. The only requirement is that the hours and minutes of a mytime can be filled in for e[0] and e[1] respectively.

The above code returns the greatest element (in this case [(9, 0), (4, 0)]).



回答3:

Are you sure using a + b works? All sum does is repeatedly apply + to adjacent elements (it's the same as reduce(operator.add, sequence) with a special case to break on strings)... So if it does work - then max(x, key=sum) should just work -- as long as mydate supports comparison operators - eg __gt__, __eq__, __lt__

Example

You need to have __gt__ defined for max to work...

class mydate(object):
    def __init__(self, num):
        self.num = num
    def __add__(self, other): # make sure sum works
        return self.num + other.num
    def __gt__(self, other): # make sure max can do > comparison
        return self.num > other.num
    def __repr__(self):
        return 'date: {}'.format(self.num)


x = mydate(3)
y = mydate(5)
z = mydate(2)

print max([x,y,z], key=sum)