I wrote a function that checks if every character on a given string is also present in an arbitrary list. The code works for most cases, but when the input is an empty string, the built-in function all
evaluates to True
, even though that value isn't present at the ITEMS
list:
ITEMS = ['a', 'b', 'c', 'd']
def check_all_chars_are_in_list(string):
return all(char in ITEMS for char in string)
if __name__ == "__main__":
print(check_all_chars_are_in_list('saban')) # False, as expected. (1)
print(check_all_chars_are_in_list('abcd')) # True, as expected. (2)
print(check_all_chars_are_in_list('')) # True (why?). (3)
I performed a manual test as to try and understand that behavior. I simply checked the in
assertion via the terminal:
>>> st = ''
>>> l = ['a', 'b', 'c', 'd']
>>> st in l
False # As expected
But I got the expected False
value, so how come the example (3) on my code evaluates to True
? Can someone explain that to me, please?
Thank you!
First of all, what does function all
do? According to docs:
Return True if all elements of the iterable are true (or if the iterable is empty). Equivalent to:
def all(iterable): for element in iterable: if not element: return False return True
That means that you have to pass iterable of boolean values to function all
.
Let's walk through your examples to see what happens. I will modify your function a little just so it prints list that is passed to function all
to see what is really passed.
ITEMS = ['a', 'b', 'c', 'd']
def check_all_chars_are_in_list(string):
temp = [char in ITEMS for char in string]
print(temp)
return all(temp)
So, for your examples:
check_all_chars_are_in_list('saban') # list temp is [False, True, True, True, False]
check_all_chars_are_in_list('abcd') # list temp is [True, True, True, True]
check_all_chars_are_in_list('') # list temp is [] (empty)
What happens in third example is that you pass empty list to function all
and by definition it returns True
. Why is temp = []
in that example? Because you are trying to loop through string
(which is empty) in list comprehension and since it has nothing to loop through it sets temp to be empty list. If you look at to what is function all
equivalent (provided in quoted docs) you can see that when you pass empty list it will have nothing to loop through so it will just return True
.
Your terminal example is not really the same as the one using function all
. Lets see what might be equivalent to checking if string is in list. What your code:
st = ''
l = ['a', 'b', 'c', 'd']
print(st in l)
is equivalent (not exactly, but logically) is this:
def check_with_in(string, l):
for el in l:
if el == string:
return True
return False
print(check_with_in('', ['a', 'b', 'c', 'd'])) # False
and since ''
is not in the list it will return False
. What using in
with lists is equivalent to according to docs:
The operators
in
andnot in
test for membership. [...] For container types such as list, tuple, set, frozenset, dict, or collections.deque, the expressionx in y
is equivalent toany(x is e or x == e for e in y)
.