#!/usr/bin/env python # coding: utf-8 # # `any` and `all` in Python # # (for Evan) # In[4]: some_true = [0, 1] all_true = [1, 1] # `all` returns True if "all items are True", otherwise False. # In[12]: all(all_true) # If any item is False, `all` returns False # In[11]: all(some_true) # What about empty collections? # In[7]: all([]) # It's ambiguous whether `all` of an empty collection should be considered true or false. However, this more precise definition should cear things up: # # > `all` returns False if any items in the collection are Falsey, True otherwise. # # This can be further clarified if we reimplement `all` with `any`, which more clearly returns True if a single True value is seen. That's more clearly False if there are no items: # In[17]: any([]) # In[18]: any(some_true) # If we implement `all` with `any`, we first construct the "any items are False" statement, by computing `any` of the inverse of each item. Then we invert that. # In[9]: def all_from_any(collection): any_false = any(not item for item in collection) return not any_false # Here, `any_false` is clearly False for an empty collection, and since we return `not any_false`, `all` is True for an empty collection: # In[10]: all_from_any([]) # In[20]: all_from_any(some_true) # In[21]: all_from_any(all_true) # We can express that in terms of [De Morgan's Law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws), which you might have learned in Boolean Logic: # # $$ # \overline{A \cdot B} \equiv \overline {A} + \overline {B} # $$ # # which, after inverting both sides, is exactly what our `all_from_any` above does: # # $$ # A \cdot B \equiv \overline{ \overline {A} + \overline {B} } # $$ # Ultimately, it's a choice what to return for empty collections.