Because garbage cycles can point at non-garbage; when the garbage is reclaimed, __del__() methods will run. You could argue that this is another reason against using __del__(), but since this is part of the way CPython works, I'm documenting it in my book.
Documenting that you can call gc.collect() at the end is good; documenting that you should call it is not.
I would expect that in many cases, it will be irrelevant whether __del__ is called at the end or not, as the system or the underlying libraries will reclaim whatever resources have been acquired. If you need to guarantee that __del__ is called at the end for all objects, you have probably much bigger problems in your application than that, and it is likely better to explicitly break any remaining cycles than to invoke gc.collect.