7

I was wondering if we could assert all elements in a list is not None, therefore while a = None will raise an error.

The sample list is [a, b, c]

I have tried assert [a, b, c] is not None, it will return True if any one of the elements is not None but not verifying all. Could you help figure it out? Thanks!!

PPP
  • 213
  • 2
  • 10
  • 1
    `assert [a, b, c] is not None` will pass *even if* all the elements are `None`. The **only** thing that `is None` is... `None`. `is` means *the same object*, not an equality check. It also is not possible to create more instances of `None`'s type. – Karl Knechtel Sep 06 '21 at 03:52
  • "assert [a, b, c] is not None, it will return True if any one of the elements is not None " **no**. that isn't what is happening, it is doing an identity check for `None`. Any list is not None, in fact, any object *except `None`* is not None. – juanpa.arrivillaga Sep 06 '21 at 03:53

3 Answers3

8

Unless you have a weird element that claims it equals None:

assert None not in [a, b, c]
no comment
  • 6,381
  • 4
  • 12
  • 30
  • kudos, as this version also seems the fastest. If anyone curious, about 5x faster than `all` and checking if each element is not `None`, but very close in terms of performance to `all([a, b, c])` – rv.kvetch Sep 06 '21 at 03:28
  • 5
    @rv.kvetch `all([a, b, c])` is significantly less safe, though, as any false value (e.g., a zero) will make that return `False`. – no comment Sep 06 '21 at 03:31
  • yep, I never said it was perfect; but somehow it performs much better than the one with the explicit `None` check, which seems slow by comparison. – rv.kvetch Sep 06 '21 at 03:35
4

Do you mean by all:

>>> assert all(i is not None for i in ['a', 'b', 'c'])
>>>

Or just simply:

assert None not in ['a', 'b', 'c']

P.S just noticed that @don'ttalkjustcode added the above.

Or with min:

>>> assert min(a, key=lambda x: x is not None, default=False)
>>>
U13-Forward
  • 69,221
  • 14
  • 89
  • 114
  • `default` should be `True`, though I find `min` very weird for this. And for example it cries AssertionError for [0, 1, 2] even though there's no None. Btw the correct faster `all(...)` that I think you were trying earlier is as expected indeed faster, see @rv's benchmark. – no comment Sep 06 '21 at 14:59
1

Midway in terms of performence between not in and all. Note that the sensible (go-to) version with all for this particular case will end up performing slow - but at least ahead of min

def assert_all_not_none(l):
    for x in l:
        if x is None:
            return False
    return True

Edit: here are some benchmarks for those intersted

from timeit import timeit


def all_not_none(l):
    for b in l:
        if b is None:
            return False
    return True


def with_min(l):
    min(l, key=lambda x: x is not None, default=False)


def not_in(l):
    return None not in l


def all1(l):
    return all(i is not None for i in l)


def all2(l):
    return all(False for i in l if i is None)


def all_truthy(l):
    return all(l)


def any1(l):
    return any(True for x in l if x is None)


l = ['a', 'b', 'c'] * 20_000

n = 1_000

# 0.63
print(timeit("all_not_none(l)", globals=globals(), number=n))

# 3.41
print(timeit("with_min(l)", globals=globals(), number=n))

# 1.66
print(timeit('all1(l)', globals=globals(), number=n))

# 0.63
print(timeit('all2(l)', globals=globals(), number=n))

# 0.63
print(timeit('any1(l)', globals=globals(), number=n))

# 0.26
print(timeit('all_truthy(l)', globals=globals(), number=n))

# 0.53
print(timeit('not_in(l)', globals=globals(), number=n))

Surprisingly the winner: all(list). Therfore, if you are certain list will not contain falsy values like empty string or zeros, nothing wrong with going with that.

rv.kvetch
  • 9,940
  • 3
  • 24
  • 53
  • 2
    Those claims really make me desire to see a benchmark :-) – no comment Sep 06 '21 at 03:43
  • yep, just added some quick and dirty one with a sufficiently long list to test – rv.kvetch Sep 06 '21 at 04:01
  • 1
    I think it's a bit misleading, as all except `all_not_none` would normally be used inline (like `if any(l):`) without extra function call, while `all_not_none` *would* be used with a call (like `if all_not_none(l)`). But alright otherwise. Could also include the `all(False ...)` one I mentioned in a comment, should be faster for the same reason as [this](https://stackoverflow.com/q/68938628/16759116). – no comment Sep 06 '21 at 04:13
  • woah, that was a huge difference. I added another test for `all` and it was really much faster. nice catch :) – rv.kvetch Sep 06 '21 at 04:24
  • The `any` should be `not any`. – no comment Sep 06 '21 at 14:49