The idiomatic empty test
if not items:
is how Python asks "is this empty?", and it is preferred over len(items) == 0 because it works on anything and reads as prose. For a list, a string or a dict, this is right and unremarkable.
Where it goes wrong
The trouble starts when a value can legitimately be 0 or "":
result = find(items, "a") # returns index 0
if not result:
print("not found") # WRONG
Index 0 is falsy, so "found at the first position" and "not found at all" take the same branch. Nothing raises. The program is simply wrong for one input, and that input is the first element, which many test cases skip.
The page runs exactly this, printing the wrong answer and then the fix.
`is None` asks a different question
if result is None:
This tests for one specific object, not for emptiness. It is true for None and nothing else — not for 0, not for "". When a function returns "the thing, or None if there isn't one", this is the only correct test.
The rule that follows: use truthiness when you mean "empty or zero or missing, and I treat them the same". Use is None when None means something distinct from a legitimate empty value.
Why `is` rather than `==`
There is exactly one None object in a running program, so identity is the precise test and it is faster than equality. It also cannot be fooled: a class can define __eq__ so that x == None is true for something that is not None. is compares the object itself.
The same applies to the two default-argument patterns from earlier in the track: if basket is None is correct, and if not basket would treat an intentionally empty list as missing.