On Thu, Oct 01, 2015 at 04:13:37PM -0700, Chris Barker wrote:
On Thu, Oct 1, 2015 at 12:37 PM, Steven D'Aprano firstname.lastname@example.org wrote:
This makes a difference. Just a few days ago, somebody mis-reported a supposed "bug" in all() and any(). For example:
values = (x%5 == 3 for x in range(8)) print(list(values)) print(all(values)) # should return False
Obvious error is obvious: having printed out the values from the generator expression, values is now exhausted, and all() of the empty set is True (vacuous truth). The difference between general iterables which may or may not be one-shot iterators, and those which are definitely not iterators, is not always just an implementation detail.
they used a generator expression, when they clearly wanted a list comprehension -- so yes, it matters what they were getting, I don't know that adding more vocabulary would help prevent people from making that mistake...
if they had been smart enough to call the list() again, before claiming there was a bug in all -- it may have explained itself.
You're missing the point. Don't focus on the fact that the bug was in their understanding of what their code did. Let's just pretend that their *intentional* algorithm was:
def alg(iterable): print(list(iterable)) print(all(iterable))
and for the sake of bring this never-ending thread to an end, let's agree for the sake of argument that it cannot be re-written in any other way.
Since the semantics of the function are intentional and correct, the parameter is named misleadingly. *iterable* is not sufficiently precise, because the function does not accept any old iterable -- it fails to work correctly on *iterators*, are a sub-kind of iterable.
If you want a more practical example, any algorithm which needs to iterate over an interable two or more times needs to specify "iterable which is not an iterator".