49

I would like to perform the following:

a=max(a,3)
b=min(b,3)

However sometimes a and b may be None.
I was happy to discover that in the case of max it works out nicely, giving my required result 3, however if b is None, b remains None...

Anyone can think of an elegant little trick to make min return the number in case one of the arguments in None?

marshall.ward
  • 6,758
  • 8
  • 35
  • 50
Jonathan Livni
  • 101,334
  • 104
  • 266
  • 359
  • 6
    It doesn't do the right thing. It happens to give the result you expect in one of two cases because the nonsensical comparision between `NoneType` and `int` returns a fixed value regardless of the integer value. In Python 3, you get a `TypeError` when you do things like that (comparing types that have no meaningful ordering). –  Jun 06 '11 at 16:11
  • 2
    Seems like an inconsistency in Python, more than anything else. – Rafe Kettler Jun 06 '11 at 16:12
  • 5
    http://stackoverflow.com/questions/2214194/is-everything-greater-than-none – Ben Jackson Jun 06 '11 at 16:13
  • if you are ok with replacing None to a default int value (like 0), you can do something like this: `max(a,3,key=lambda x: x or 0)` – Shay Tsadok Jan 23 '20 at 08:31

11 Answers11

53

Why don't you just create a generator without None values? It's simplier and cleaner.

>>> l=[None ,3]
>>> min(i for i in l if i is not None)
3
utdemir
  • 26,532
  • 10
  • 62
  • 81
  • 3
    No need for the list comprehension. Still +1 (in advance - seriously, please remove the brackets), it's a clean and simple solution. –  Jun 06 '11 at 16:18
  • Thanks, without the braces, does Python interprets it like a generator expression? – utdemir Jun 06 '11 at 16:22
  • 3
    Yes. The parens around generator expressions are optional if the genexpr is the sole argument to a function call. –  Jun 06 '11 at 16:27
  • 16
    One potential problem with a solution like this is that it works fine for the listed example but if you have a list with `[None, None]`, the `min()` function will fail because you're not giving it a valid argument. – Kevin London May 12 '16 at 17:29
  • 6
    @KevinLondon, specify the default value for the min as 0 or None depending upon the behavior you want. ```min((x for x in [None,None] if x is not None), default=None)``` – rahul.deshmukhpatil Dec 28 '20 at 08:56
9

My solution for Python 3 (3.4 and greater):

min((x for x in lst if x is not None), default=None)
max((x for x in lst if x is not None), default=None)
R1tschY
  • 3,032
  • 1
  • 24
  • 30
7

A solution for the Python 3

Code:

# variable lst is your sequence

min(filter(lambda x: x is not None, lst)) if any(lst) else None

Examples:

In [3]: lst = [None, 1, None]

In [4]: min(filter(lambda x: x is not None, lst)) if any(lst) else None
Out[4]: 1

In [5]: lst = [-4, None, 11]

In [6]: min(filter(lambda x: x is not None, lst)) if any(lst) else None
Out[6]: -4

In [7]: lst = [0, 7, -79]

In [8]: min(filter(lambda x: x is not None, lst)) if any(lst) else None
Out[8]: -79

In [9]: lst = [None, None, None]

In [10]: min(filter(lambda x: x is not None, lst)) if any(lst) else None

In [11]: print(min(filter(lambda x: x is not None, lst)) if any(lst) else None)
None

Notes:

Worked in sequence presents as numbers as well as None. If all values is None min() raise exception

ValueError: min() arg is an empty sequence

This code resolve this problem at all

Pros:

  1. Worked if None presents in sequence
  2. Worked on Python 3
  3. max() will be work also

Cons

  1. Need more than one non-zero variable in the list. i.e. [0,None] fails.
  2. Need a variable (example lst) or need duplicate the sequence
André Guerra
  • 486
  • 7
  • 22
PADYMKO
  • 4,217
  • 2
  • 36
  • 41
4

Here is a decorator that you can use to filter out None values that might be passed to a function:

def no_nones(fn):
    def _inner(*args):
        return fn(a for a in args if a is not None)
    return _inner

print no_nones(min)(None, 3)
print no_nones(max)(None, 3)

prints:

3
3
PaulMcG
  • 62,419
  • 16
  • 94
  • 130
3
def max_none(a, b):
    if a is None:
        a = float('-inf')
    if b is None:
        b = float('-inf')
    return max(a, b)

def min_none(a, b):
    if a is None:
        a = float('inf')
    if b is None:
        b = float('inf')
    return min(a, b)

max_none(None, 3)
max_none(3, None)
min_none(None, 3)
min_none(3, None)
Steve Mayne
  • 22,285
  • 4
  • 49
  • 49
  • Note that this only works if the default is 0. Passing 0 instead of `None` still triggers passing the default but doesn't change the value since the default is 0 (and `0 == 0.0 == 0j`). –  Jun 06 '11 at 16:16
  • 1
    The OP said max was not a problem. Also what happens if b is negative ? – Pavan Yalamanchili Jun 06 '11 at 16:16
2

You can use an inline if and an infinity as the default, as that will work for any value:

a = max(a if a is not None else float('-inf'), 3)
b = min(b if b is not None else float('inf'), 3)
Blender
  • 289,723
  • 53
  • 439
  • 496
1
a=max(a,3) if a is not None else 3
b=min(b,3) if b is not None else 3
Steve Mitchell
  • 1,895
  • 1
  • 15
  • 12
1

@utdemir's answer works great for the provided example but would raise an error in some scenarios.

One issue that comes up is if you have a list with only None values. If you provide an empty sequence to min(), it will raise an error:

>>> mylist = [None, None]
>>> min(value for value in mylist if value)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: min() arg is an empty sequence

As such, this snippet would prevent the error:

def find_minimum(minimums):
    potential_mins = (value for value in minimums if value is not None)
    if potential_mins:
        return min(potential_mins)
Kevin London
  • 4,628
  • 1
  • 21
  • 29
0

I believe the cleanest way is to use filter built-in function

a = max(filter(None, [a, 3]))
b = min(filter(None, [b, 3]))
Sam
  • 791
  • 6
  • 9
0

If you can use np.nan instead of None:

import numpy as np
float(np.nanmax([a, np.nan]))
float(np.nanmin([a, np.nan]))
alEx
  • 193
  • 6
  • 12
0

Based on @PADYMKO's answer you can also use numpy array .any() to filter zeros from None if your array contains both :

>>> lst = np.asarray(lst)
>>> min(filter(lambda x: x is not None, lst)) if (lst != None).any() else None

Results :

>>> lst = [None, None]
>>> lst= np.array(lst)
>>> min(filter(lambda x: x is not None, lst)) if (lst != None).any() else None

>>> lst = [0, None]
>>> lst= np.array(lst)
>>> min(filter(lambda x: x is not None, lst)) if (lst != None).any() else None
0

>>> lst = [0, -5, 2, None]
>>> lst= np.array(lst)
>>> min(filter(lambda x: x is not None, lst)) if (lst != None).any() else None
-5
Aldhra
  • 1
  • 2