Question

This question is close to mine, but not quite: List minimum in Python with None?. I need to allow for the possibility of all values in the list being None.

I have a situation where I have to look up some numbers in a database, and they may either exist or come back as null. I need to find the minimum of those existing numbers. For example:

min_if_exists(1, 2) returns 1
min_if_exists(5, None) returns 5
min_if_exists(None, None) returns None

Luckily I can do something like this:

def min_if_exists(a, b):
    return min(a, b)
elif a:
    return a
elif b:
    return b
else:
    return None

The following is the answer to the question referenced above:

lowest = min(x for x in (a, b) if x is not None)

With this, however, if a and b are None, I get an exception because I inputted an empty sequence. I could simply catch the exception, or I could include an arbitrarily large number in the sequence (a, b, 100000000). For a arbitrarily long sequence of numbers that may all be None, I could (presumably) un-Pythonically do this:

def min_if_exists(list_of_nums):
    not_a_none_exists = False
    for n in list_of_nums:
        if n is not None:
            not_a_none_exists = True
    if not_a_none_exists:
        return min(x for x in list_of_nums if x is not None)
    else:
        return None

Is there a more Pythonic, more concise way of doing this?

Était-ce utile?

La solution

Try this, it's a pythonic solution:

def min_if_exists(list_of_nums):
    try:
        return min(x for x in list_of_nums if x is not None)
    except ValueError:
        return None

Remember: in Python, it's easier to ask for forgiveness than permission! quoting the documentation:

This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL (look before you leap) style common to many other languages such as C.

Autres conseils

If you wait for Python 3.4, you can do this quite cleanly as min and max will grow a default parameter (see issue #18111):

>>> def min_not_none(seq):
...     return min((x for x in seq if x is not None), default=None)
... 
>>> print(min_not_none([3,None,1]))
1
>>> print(min_not_none([None]))
None

I think the exception-handling-based answer by Oscar Lopez is a very good option. Here is an alternative (probably inferior) one:

def my_min(lst):
    if lst == [None]*len(lst):
        return None
    else:
        return min(x for x in lst if x is not None)

print(my_min([0,2,-1]))           # -1
print(my_min([0,2,None]))         # 0
print(my_min([None, None, None])) # None
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top