Behaviour of Python all() operator [duplicate] - python

This question already has answers here:
Python "all" function with conditional generator expression returning True. Why?
(2 answers)
Closed 9 years ago.
have a question on all() operator in Python.
say
array = ["one","one","one"]
all( x=="one" for x in array ) <<--- i want to check for all "one" in array
The above seem to work. however, if i have
array = []
all( x=="one" for x in array ) <<--- this still return true to me.
The behaviour is that i want it return false if all items are not "one". How to do it? thanks

You can read all() as if it means:
It returns False if any of the items evaluates to False. True otherwise.
So an empty set will return True, because there is none that will make it false.
Generally speaking, in an empty set, all the elements fullfill any requirement you can imagine. That's a principle of logic, not of Python, BTW.

all's implementation is equivalent to this
def all(iterable):
for element in iterable:
if not element:
return False
return True
So, it returns True till any of the elements in the iterable is Falsy. In your case that didnt happen. Thats why it returns True

all always returns True for an empty list/tuple/etc. This is because, technically, every item in an empty collection fulfills any and every condition there is.
To fix the problem, you need to add some additional code to test whether your list is empty or not. Fortunately, empty lists evaluate to False in Python, so you can just do this:
>>> array = []
>>> bool(array and all(x=="one" for x in array))
False
>>> if array and all(x=="one" for x in array):
... print True
... else:
... print False
...
False
>>>

How to do it?
array and all(x=="one" for x in array)
Empty lists are false, so the result is false and it doesn't matter that the all part is true.
If you want to deal with iterables other than containers like list then it's a bit harder. I suppose you need something like this:
set(x=="one" for x in iterable) == { True }
Although if you care about speed, the following should be faster on the whole, since the version above doesn't short-circuit like all does:
def nonempty_all(iterable):
iterator = iter(iterable)
try:
if not next(iterator):
return False
except StopIteration:
return False
return all(iterator)

Related

I don't fully understand how the any() function in this script works

I have a script that checks if there are one or more of the same items in a list. Here's the code:
items = ["Blue", "Black", "Red"]
def isUnique(item):
seen = list()
return not any(i in seen or seen.append(i) for i in item)
print(isUnique(items))
It prints "True" if all the items in the given list are unique and "False" if one or more items in the list are unique. Can someone please explain the any() part of the script for me as I don't fully understand how it works?
This code is kind of a hack, since it uses a generator expression with side-effects and exploits the fact that append returns None, which is falsy.
The equivalent code written in the imperative style is like so:
def isUnique(items):
seen = list()
for i in items:
if i in seen or seen.append(i):
return False
return True
The or is still a bit strange there - it is being used for its short-circuiting behaviour, so that append is only called when i in seen is false - so we could rewrite it like this:
def isUnique(items):
seen = list()
for i in items:
if i in seen:
return False
else:
seen.append(i)
return True
This is equivalent because append is only called when i in seen is false, and the call to append returns None which means the return False line shouldn't execute in that case.
Here you need to understand first how or operator works.
or is like exp1 or exp2
it just evaluates the expression which gives True first or give true at last
eg
>>> 2 or 3
2
>>> 5 or 0.0
5
>>> [] or 3
3
>>> 0 or {}
{}
now for your list comprehension, [i in seen or seen.append(i) for i in items] i in seen evaluate false and seen.append(i) True and which return None ie list.append return None so , comprehension contain all None
>>> seen = []
>>> items = ["Blue", "Black", "Red"]
>>> res = [i in seen or seen.append(i) for i in items]
>>> res
[None, None, None]
>>> any(res)
False
as per any documentation, it is returning false beacuse as it is not getting iterable or bool.
>>> help(any)
Help on built-in function any in module builtins:
any(iterable, /)
Return True if bool(x) is True for any x in the iterable.
If the iterable is empty, return False.
the any function in python takes a list of booleans and returns the OR of all of them.
the i in seen or seen.append(i) for i in item appends i to seen if it's not in seen already. but if it is already in seen then the append() does not run since the first part is already True, and python doesn't need to know if the second part is true since True OR'd with anything is True. so it doesn't execute it. so the seen array ends up being a unique list of colours it has seen.
i in seen or seen.append(i) for i in item is also a generator expression,
which generates booleans, and any checks the booleans it generates, if even one of them evaluates to True, the whole any will return True.
so the first time an item that is already in the seen array is found, any will stop the generator and return True itself.
so if a duplicate element happens to be in the array no more conditions are evaluated and no more elements are appended to seen array
so if the array had duplicate elements, like,
items = ["Blue", "Blue", "Black", "Red"]
def isUnique(item):
seen = list()
unique = not any(i in seen or seen.append(i) for i in item)
print(seen)
return unique
isUnique(items)
would result in the output, just
['Blue']
EDIT: there are great answers. Adding some simpler ways to achieve the wanted result:
Method 1:
items = ["Blue", "Black", "Red"]
items_set = set(items)
if len(items_set) != len(items):
# there are duplications
This works because a set object ‘removes’ duplications.
Method 2:
contains_duplicates = any(items.count(element) > 1 for element in items) # true if contains duplications and false otherwise.
See https://www.kite.com/python/answers/how-to-check-for-duplicates-in-a-list-in-python
———————————————
any is a great function
Return True if any element of the iterable is true. If the iterable is empty, return False
Your function isUnique, however, does a bit more logic. Let's break it down:
First you create an empty list object and store it in 'seen' variable.
for i in item - iterates the list of items.
i in seen - This statement returns True if 'i' is a member of 'seen', and false otherwise.
seen.append(i) - add i to seen. This statement returns None if 'i' is appeneded to seen successfully.
Notice the or statement between i in seen or seen.append(i). That means, if one of the statements here is True, the or statement returns True.
At this point, I'd run [i in seen or seen.append(i) for i in item], see the result and experiment with it. The result for your example is [None, None, None].
Basically, for each item, you both add it to the list and check if it is already in the list.
Finally, you use the any() function - which returns True if the iterable has a True value. This will happen only if i in seen will return True.
Notice you are using not any(...), which returns False in case there are no repititions.
There are simpler and clearer ways to implement this. You should try!
It is quite simple: the expression inside any() is a generator. any() draws from that generator and returns True (and stops) at the first element from the generator that is True. If it exhausts the generator, then it returns False.
The expression in the generator (i in seen or seen.append(i)) is a trick to express as a one-liner the logic that: if i is in the list, the expression is True and any() stops immediately, otherwise, i is added to the list and the generator continues.
The function can be significantly improved by using a set instead of a list:
def isUnique(item):
seen = set()
return not any(i in seen or seen.add(i) for i in item)
It is much faster to test for presence of an item in a set (O[1]) than in a list (O[n]).
One interesting and perhaps underappreciated aspect of this code is that it works on a (potentially infinite) generator. It will stop drawing from the generator at the first repeated item. Subsequent items that would be obtained by the generator are not evaluated at all (with potential side-effects, desirable or not).
A different approach, suitable for known and finite collections of items, would be the following:
def isUnique(items):
items = tuple(items) # in case items is a generator
return len(set(items)) == len(items)
This assumes that all the items fit in memory. Obviously this won't work if items is a generator of a very large or infinite number of elements.

if decision with list variable [duplicate]

This question already has answers here:
What is Truthy and Falsy? How is it different from True and False?
(8 answers)
Closed 3 years ago.
In C/C++, true is standardized as 1, and false as 0. Although not a good practice,
if variable:
#do something
kind of decision making seems ok in python and it seems makes decision based on whether variable is None or is zero - if it is a number. Followings are reasonable.
a = 123123
if a:
print "if condition is true" # this prints
a = "a string"
if a:
print "if condition is true" # this prints
a = None
if a:
print "if condition is true" # this does not print
However, I wonder why it evaluates to false with an empty list, which is neither None, nor zero. What is the exact implementation of python if statement?
a = [] # a is not None
if a:
print "true" # this does not print
An if statement requires a bool. That means the the expression following the if is analyzed is a boolean context. Said differently it is converted to bool.
If a bool context, the following items evaluate to False (non exhaustive list):
None
numeric 0
empty list
empty string
empty dict
Non null numerics and non empty iterables eveluate to True
Lists are empty, so they're False, that's how python reacts, to know whether something is False so doesn't go trough, do:
>>> bool([])
False
>>>
So it is False.

Python any() function does not act as expected for list of negative numbers

I am trying to compare a list of numbers in an if statement with the any() function. I am using python 3.6 in Spyder. The code in question is:
if any(lst) >= 1:
do_some_stuff
lst is actually generated by list(map(my_func, other_lst)) but after diagnosing my problem I get two behaviors as shown below with my actual lst passed to the any() function:
any([1.535, 1.535, 1.535]) >= 1
>>True
Which is expected.
any([-0.676, -0.676, -0.676]) >= 1
>>True
Which is not expected.
I've delved deeper and found that any number I can put in lst that is less than 0 yields True in my if statement. Also, converting the 1 to a float doesn't help. After reading "truth value testing", this post, and extensive time trying to learn the behavior within my program, I am at a loss. Please help, I am pretty new to python. Thanks!
You are comparing the wrong thing. You are comparing the result of any to 1.
any(mylist) will return true if any element of the list is nonzero. True is greater than or equal to 1.
So this
any(mylist)>=1
is equivalent to just
any(mylist)
What you mean is
any(x>=1 for x in mylist)
Please read the documentation:
any(iterable)
Return True if any element of the iterable is true. If
the iterable is empty, return False. Equivalent to:
def any(iterable):
for element in iterable:
if element:
return True
return False
All your any() expression listed will return True, and you are comparing True with 1, that is True.

python if/else logic explained please

I have a really basic if/else logic question for Python. An exercise in the book required me to write a function that takes a list of integers are returns True if the list contains all even numbers and False if it doesn't.
I wrote:
list1 = [8,0,-2,4,-6,10]
list2 = [8,0,-1,4,-6,10]
def allEven(list):
for x in list:
if x % 2 != 0:
return False
else:
return True
This code always returns True. Why is that? Doesn't the code see the -1 during the loop of all the values of the list and returns the False?
list1 = [8,0,-2,4,-6,10]
list2 = [8,0,-1,4,-6,10]
def allEven(list):
for x in list:
if x % 2 != 0:
return False
return True
The book gives the answer of this. Why does this work and mine doesn't?
Pay close attention to where that else is placed. Indentation and nesting matters here!
In your first example, it will return True on the first element that satisfies your condition because your first if check fails.
In your second example, it will return True after all elements have been iterated through and a return value hasn't been produced.
The first function checks the first number only, since it returns something as soon as the for loop starts.
By the way, you can but should not use list as an argument or a variable name, since it is a keyword.
I strongly recommend writing a print statement to output x before both of your return statements. It will help you understand the flow of the code.
The short answer is that only the first element is being checked by your code, and the function returns True or False based on that value.
In the book solution, any failure causes a return of False, but the loop simply continues otherwise. Only if all elements are checked without failure does the return True reached.

Boolean testing a list in Python

I was testing a list to see if it's empty or not. Normally I use len(list) == 0 and I vaguely remembered reading a little while ago that the correct way to test if a list is empty was whether it was True or false.
So I tried list is False, and that returned False. Maybe I'm suppose to be using == ?
Nope, that also returned false. list is True, returned false as did list == True.
Now I'm confused so I do a quick google and end up at: Best way to check if a list is empty
The top answer is:
if not a:
print "List is empty"
So I search around some more and end up in the python manual where 4.1 states:
Any object can be tested for truth value, for use in an if or while condition or as operand of the Boolean operations below. The following values are considered false:
any empty sequence, for example, '', (), [].
Now I'm plain confused. If I test a list as if not list, it works fine. But if an empty list is false, then why can't I just do if list is False or if list == False?
Thanks
An empty list is not False, but when you convert it to a boolean, it converts to False. Likewise for dicts, tuples, strings, etc.:
>>> [] == False
False
>>> bool([]) == False
True
>>> {} == False
False
>>> bool({}) == False
True
When you put something in the condition of an if clause, it is its boolean value that is used for testing the if. That's why if someList is the same as if bool(someList). Likewise, not foo does a boolean not, so not [] equals True.
As other have said, in python bool([]) == False. One thing that is frequently exploited by python programmers is that the operators and and or don't (necessarily) return True/False. Consider the following:
3 and 4 #returns 4
0 and 8 #returns 0 -- This is short-circuit evaluation
0 or 8 #returns 8
True or 0 #returns True -- This is short-circuit evaluation
[] or False #returns False
False or [] #returns []
What happens in an if statement is that the condition gets evaluated as above and then python implicitly calls bool on the result -- So you can think of it as:
if condition:
is the same thing as:
if bool(condition):
as far as python is concerned. Similarly for the not operator:
not condition
is the same thing as
not bool(condition)
mylist is False means "is the object named mylist exactly the same object as False?"
mylist == False means "is the object named mylist equal to False?
not mylist means "does the object named mylist behave falsily?
None of these are equivalent: 1 is not 1.0 but 1 == 1.0 and [] != False but not [] is True.
Comparing the list to False, and testing the list's truth or falsehood aren't quite the same thing. An empty list isn't equal to False, but behaves as False in a boolean context.
Here's another way to say it that might help this make sense:
print (bool([]) == False) # will print True
print ([] == False) # will print False

Categories

Resources