131

If I have a construct like this:

def foo():
    a=None
    b=None
    c=None

    #...loop over a config file or command line options...

    if a is not None and b is not None and c is not None:
        doSomething(a,b,c)
    else:
        print "A config parameter is missing..."

What is the preferred syntax in python to check if all variables are set to useful values? Is it as I have written, or another better way?

This is different from this question: not None test in Python ... I am looking for the preferred method for checking if many conditions are not None. The option I have typed seems very long and non-pythonic.

Community
  • 1
  • 1
Eyelash
  • 1,760
  • 2
  • 12
  • 18
  • 2
    Possible duplicate of [not None test in Python](http://stackoverflow.com/questions/3965104/not-none-test-in-python) – user2390182 Feb 21 '17 at 07:21
  • 1
    I am checking for four variables and was, as well, thinking the obvious solution you posted is not very pythonic. I am sad having to conclude from the lack of answers that there doesn't seem to be a "nicer" way. – wedi Apr 11 '17 at 14:01
  • 5
    @schwobaseggl: No, this is not a duplicate as the question is not about how to check for None. It is about how to make it "look more pythonic" when checking multiple variables but not as many to rectify the proposed solution by Daniel Roseman. – wedi Apr 11 '17 at 14:05
  • def all_is_not_None(*args): return all(x is not None for x in args) – iperov Mar 04 '21 at 13:13

4 Answers4

169

It can be done much simpler, really

if None not in (a, b, c, d):
    pass

UPDATE:

As slashCoder has correctly remarked, the code above implicitly does a == None, b == None, etc. This practice is frowned upon. The equality operator can be overloaded and not None can become equal to None. You may say that it never happens. Well it does not, until it does. So, to be on the safe side, if you want to check that none of the objects are None you may use this approach

if not [x for x in (a, b, c, d) if x is None]:
    pass

It is a bit slower and less expressive, but it is still rather fast and short.

serge.v
  • 1,854
  • 1
  • 11
  • 7
  • 6
    Yes! And it's 10 times faster too! (According to `timeit`) – Praind Oct 16 '18 at 14:29
  • 4
    The problem with this is that the `in` and `not in` operators do equality checks against each element of the collection, which is advised against in PEP8. – slashCoder Sep 30 '19 at 16:20
  • 3
    Please be aware this is a Yoda Condition(https://en.wikipedia.org/wiki/Yoda_conditions) and must be used with caution. – Chandrahas Aroori Feb 11 '20 at 06:11
  • 4
    @Chandrahas Aroori: Hrrmmm. Exercise the caution you must! – serge.v Mar 27 '20 at 12:39
  • 1
    In Pandas/Dask this yields: `ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()` [related](https://stackoverflow.com/questions/10062954/valueerror-the-truth-value-of-an-array-with-more-than-one-element-is-ambiguous) – gies0r Jan 18 '21 at 22:08
  • @gies0r: Could you provide relevant code, please? I am very curious, because I have tried to substitute tuple with pandas Series (s = pd.Series((1, 2, 3, None), dtype=object)) and I didn't get ValueError. Although the first approach gave the wrong result, which is exactly what slashCoder warned about. Anyway, all this was meant exclusively for the affirming of None absence in primitive sequences like tuples and lists. In pandas you have isna(), so (not s.isna().any()) is more idiomatic and may be even faster approach. – serge.v Jan 29 '21 at 23:50
  • Checking for object identity, using a set: `id(None) in {id(_) for _ in (a, b, c, d)}`. – Karl Knechtel Aug 15 '22 at 06:06
  • @Karl Knechtel. Cool! But comparing ids is not very Pythonic, is it? Also, maybe using a list instead of a set is more efficient for not that large original iterable (a, b, c, d)? – serge.v Aug 16 '22 at 07:39
  • Not very :) And of course when performance is critical, always profile for your specific use case / distribution of use cases. – Karl Knechtel Aug 16 '22 at 10:32
  • Regarding Pandas/Dask (also Numpy): questions about this are very frequently asked, but the advice is fragmented by use case. The underlying principle is: because operators are broadcast across arrays, including `==`, when the array is compared to an element of the list or set you get an array of elementwise comparison results - and the library guards against converting that to bool (using it in an `if` comparison). For the current context: that implicitly prevents stuff like `in` from working. – Karl Knechtel Aug 16 '22 at 10:38
  • I'll use this inspite "it doesn't happen, until it happens". – Danijel Feb 03 '23 at 10:09
123

There's nothing wrong with the way you're doing it.

If you have a lot of variables, you could put them in a list and use all:

if all(v is not None for v in [A, B, C, D, E]):
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
17

I know this is an old question, but I wanted to add an answer which I believe is better.

If all elements which have to be checked are hashable, you could use a set instead of a list or tuple.

>>> None not in {1, 84, 'String', (6, 'Tuple'), 3}

This is much faster than the methods in the other answers.

$ python3 -m timeit "all(v is not None for v in [1, 84, 'String', (6, 'Tuple'), 3])"
200000 loops, best of 5: 999 nsec per loop

$ python3 -m timeit "None not in [1, 84, 'String', (6, 'Tuple'), 3]"
2000000 loops, best of 5: 184 nsec per loop

$ python3 -m timeit "None not in (1, 84, 'String', (6, 'Tuple'), 3)"
2000000 loops, best of 5: 184 nsec per loop

python3 -m timeit "None not in {1, 84, 'String', (6, 'Tuple'), 3}"
5000000 loops, best of 5: 48.6 nsec per loop

Another advantage of this method is that it gives you the correct answer even if someone defines the __eq__ method of a class to always return True. (Of course, if they define the __hash__ method to return hash(None), this method won't work. But nobody should do that, because it would defeat the purpose of defining a hash.)

class Int(int):
    def __eq__(self, other):
        return True
    def __hash__(self):
        return hash(super())

print(all(v is not None for v in [1, Int(6), 2])) # True (correct)
print(None not in [1, Int(6), 2])                 # False (wrong)
print(None not in (1, Int(6), 2))                 # False (wrong)
print(None not in {1, Int(6), 2})                 # True (correct)
tfpf
  • 612
  • 1
  • 9
  • 19
  • why is dict faster than list and tuple? – zheyuanWang Jul 23 '20 at 07:41
  • 3
    @zheyuanWang To check whether an an item is present in a list, the item is compared with each element of the list. That is `O(n)`. In case of a set, however, Python simply computes the hash of the item, and checks whether the set has a valid entry for said hash. This is `O(1)`. – tfpf Jul 25 '20 at 09:48
8

EDIT: As noted by Mo2, this solution will fail if any variable is set to 0 and 0 is considered a 'useful' value.

For the specific case presented by the OP

if not all([a, b, c])

will be enough.

all([a, b, c])

evaluates to False if any parameter is missing.

papok
  • 119
  • 1
  • 5