`bool` on a generator expression in python - python

Looking to understand the meaning of bool on a generator expression in python.
bool(x for x in []) yields True
bool(x for x in [1, 2, 3]) yields True
bool(range(0)) yields False (??)
bool(range(3)) yields True

The confusion arises from range not returning a generator.
In general generators are just objects and therefore are always truthy (as opposed to containers which are False when empty).
If you do type(range(0)) you get "range", but it's not a generator, and apparently has an implicit truth value when it is empty vs non empty.

bool returns true for 'truthy' values and false for 'falsy' values.
Case 1:
(x for x in [])
Creates a generator object.
Ouput:
<generator object <genexpr> at 0x03DCDEF0>
Since an object is generated, its Truthy and it returns True.
Case 2:
(x for x in [1, 2, 3])
Same as above
Case 3:
(range(0))
This is just range(0, 0).
Try this:
for i in range(0): print(i)
There will be no output.
You will not get any value, which is Falsy and False is returned.
Case 4:
(range(3))
Try this:
for i in range(3): print(i)
Ouput:
0
1
2
Values are returned which means its truthy and True is returned.
More on Truth Value Testing

Related

Issue with the Python any() function

I am trying to convert a list of lists into a single list with '1's if any of the elements in a single list is 1 and 0 otherwise. I have the following list:
result =[[-1, -1, 0], [1, 0, -1], [1, 1, 0]]
and if I use any() on the first list
i.e. result[0], I get True even though it is supposed to be false. any(result[0]) gives True.
However, when I test each element separately, I get False in each case
i.e. result[0][0] == True gives False and similar result for the remaining elements. I can't seem to understand what I'm doing wrong.
any tests if any value is truthy, not if any value equals True. All non-zero integers are truthy so any([-1, -1, 0]) is True.
See for details Truth Value Testing which applies to any and all as well.
First, why your attempts didn't work:
bool(X) = True if x != 0; else False.
Then any(result[0]) will return True, as there is at least one non-zero element in the list.
Your second test result[0][0] == True cannot work, as result[0][0] = -1, then not True (Not even boolean in fact). So it returns False.
To do what you want, you can use any with list comprehension:
result = [1 if any(y == 1 for y in x) else 0 for x in result]
>>> [0, 1, 1] For your example
EDIT:
According to Kelly Bundy's comment, another way to do it:
result = [1 if 1 in x else 0 for x in result]
You can also use boolean, but you only can reach True/False (or 1/0)

Or keyword inside a print statement in Python?

How does Python decide the output of this ?
print([1, 2] or ["hello"])
I mean why will always print([2] or ["az"]) output [2] and not ["az"] ?
Since those lists contain elements, they will evaluate to True so Python prints whichever True literal comes first.
There are two things you have to understand here. First:
x or y
If x is truthy, it has the value of x (without even evaluating y, so 23 or launch_nukes() doesn't launch the nukes).
Otherwise, it has the value of y.
Or, as the docs put it:
The expression x or y first evaluates x; if x is true, its value is returned; otherwise, y is evaluated and the resulting value is returned.
Notice that it uses the word "true" here, not the value True. This is a bit confusing (even more so if you're talking out loud, or typing in ASCII without formatting…), which is why everyone says "truthy".
So, what do "truthy" and "falsey" mean?1
"x is truthy" does not mean x == True, it means bool(x) == True.
"x is falsey" does not mean x == False, it means bool(x) == False.
For all builtin and stdlib types:
False is falsey.
None is falsey.
Numeric zero values are falsey.
Empty containers are falsey.
Everything else is truthy.
Notice that None and empty containers are falsey, but they're not equal to False.
By convention, third-party types (including types that you define2) should follow the same rules. But sometimes there are good reasons not to. (For example, a NumPy array is neither truthy nor falsey.3)
This is covered loosely in the same docs section:
In the context of Boolean operations, and also when expressions are used by control flow statements, the following values are interpreted as false: False, None, numeric zero of all types, and empty strings and containers (including strings, tuples, lists, dictionaries, sets and frozensets). All other values are interpreted as true. User-defined objects can customize their truth value by providing a __bool__() method.
The exact details for all builtin types are buried in the standard type hierarchy, which is where you learn things like whether bytes is covered by "strings and containers" (yes) or whether there's anything special about NotImplemented (nope, it's truthy).
So, let's go through your examples:
[1, 2] or ["hello"]
Since [1, 2] is a non-empty container, it's truthy. So this equals [1, 2].
[] or ["hello"]
Since [] is an empty container, it's falsey. So this equals ["hello"].
[] == False
[] may be falsey, but it's not False, or even equal to False. Only numbers equal other numbers, and False is the number 0 in the numeric type bool,4 but [] is not a number. So this is False.
Just be glad you didn't ask about is. :)
1. Technically, these terms aren't defined, even though everyone, even the core devs, uses them all the time. The official reference defines things in terms of evaluating to true or false as a boolean, and then explains what that means elsewhere.
2. You can control whether your types' values are truthy by defining a __bool__ method—or by defining __len__. The only things you're allowed to do are return True, return False, or raise an exception; if you try to return anything different, it raises a TypeError. So, everything is either truthy, or falsey, or untestable.
3. If you try to check its truthiness, it will raise an exception. That's because NumPy uses boolean arrays widely—e.g., array([1, 2]) < array([2, 1]) is array([True, False]), and you don't want people writing if array([1, 2]) < array([2, 1]):, since whatever they're expecting it to do probably doesn't make sense.
4. Yes, bool is a numeric type—in fact, a subclass of int. This is a little weird when you're first learning, but it turns out to be useful more often than it's dangerous, so it's not just preserved for historic reasons.
x or y [or z or z1 or z2 or ...] returns the first Truthy element in sequence, or the last Falsey element if all are Falsey.
x and y [and z and z1 and z2 and ...] returns the first Falsey element in sequence, or the last Truthy element if all are Truthy.
Python has a notion of Truthiness and Falsiness that is separate from True and False. An empty list is not False, but it is Falsey. bool(something_truthy) == True and bool(something_falsey) == False.
Most things are Truthy, so it's easier to list the Falsey things:
0 (note that -1 is Truthy)
None
Empty collections ([], {}, set(), "", and etc. Note that non-empty collections containing entirely Falsey elements are still truthy e.g. [None, None, None, None])
False
Everything else is Truthy.
In your example: [1, 2] or ["hello"] == [1, 2] because the first element, [1, 2 is Truthy (the fact that ["hello"] is also Truthy is irrelevant in this case). Note that [1, 2] and ["hello"] == ["hello"].

Is any() evaluated lazily?

I am writing a script in which i have to test numbers against a number of conditions. If any of the conditions are met i want to return True and i want to do that the fastest way possible.
My first idea was to use any() instead of nested if statements or multiple or linking my conditions. Since i would be satisfied if any of the conditions were True i could really benefit from any() being lazy and returning True as soon as it could.
Based on the fact that the following print happens instantly and not after 10 (= 0 + 1 + 2 + 3 + 4) seconds i assume it is. Is that the case or am i somehow mistaken?
import time
def some(sec):
time.sleep(sec)
return True
print(any(some(x) for x in range(5)))
Yes, any() and all() short-circuit, aborting as soon as the outcome is clear: See the docs:
all(iterable)
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
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
While the all() and any() functions short-circuit on the first "true" element of an iterable, the iterable itself may be constructed in a non-lazy way. Consider this example:
>> any(x == 100 for x in range(10**8))
True
This will take several seconds to execute in Python 2 as range(10**8) constructs a list of 10**8 elements. The same expression runs instantly in Python 3, where range() is lazy.
As Tim correctly mentioned, any and all do short-circuit, but in your code, what makes it lazy is the use of generators. For example, the following code would not be lazy:
print(any([slow_operation(x) for x in big_list]))
The list would be fully constructed and calculated, and only then passed as an argument to any.
Generators, on the other hand, are iterables that calculate each item on demand. They can be expressions, functions, or sometimes manually implemented as lazy iterators.
Yes, it's lazy as demonstrated by the following:
def some(x, result=True):
print(x)
return result
>>> print(any(some(x) for x in range(5)))
0
True
>>> print(any(some(x, False) for x in range(5)))
0
1
2
3
4
False
In the first run any() halted after testing the first item, i.e. it short circuited the evaluation.
In the second run any() continued testing until the sequence was exhausted.
Yes, and here is an experiment that shows it even more definitively than your timing experiment:
import random
def some(x):
print(x, end = ', ')
return random.random() < 0.25
for i in range(5):
print(any(some(x) for x in range(10)))
typical run:
0, 1, 2, True
0, 1, True
0, True
0, 1, 2, 3, True
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, False
No. All and Any support shortcircuiting but they don't make the conditionals interpretation lazy.
If you want an All or Any using lazy evaluation you need to pass them a generator. Or else the values get evaluated in the moment the list/set/iterator/whatever is constructed
JoshiRaez is the actual correct answer.
Here is an example
a = []
any([True, a[0]])
will fail
On the other side, using OR (or AND) will not fail since its not a function:
a = []
True or a[0]

any() function in Python

I am new to Python and was playing with it until I have a problem with any() function. According to the Python library, the code for it is:
def any(iterable):
for element in iterable:
if element:
return True
return False
I created a list: list = [-1, 1] and I expected:
print any(list) < 0
print any(x < 0 for x in list)
would print two True's and the two statements are equivalent. But instead Python printed
False
True
Why is the first statement False? How is it different from the second one?
any(list) returns a boolean value, based only on the contents of list. Both -1 and 1 are true values (they are not numeric 0), so any() returns True:
>>> lst = [1, -1]
>>> any(lst)
True
Boolean values in Python are a subclass of int, where True == 1 and False == 0, so True is not smaller than 0:
>>> True < 0
False
The statement any(list) is in no way equivalent to any(x < 0 for x in list) here. That expression uses a generator expression to test each element individually against 0, and there is indeed one value smaller than 0 in that list, -1:
>>> (x < 0 for x in lst)
<generator object <genexpr> at 0x1077769d8>
>>> list(x < 0 for x in lst)
[False, True]
so any() returns True as soon as it encounters the second value in the sequence produced.
Note: You should avoid using list as a variable name as that masks the built-in type. I used lst in my answer instead, so that I could use the list() callable to illustrate what the generator expression produces.
As stated in the docs, any(list) returns a boolean. You're comparing that boolean to the integer 0:
>>> any(list)
True
>>> True < 0
False

Strange conversion in Python logic expressions

I noticed a strange behavior of Python 2.7 logic expressions:
>>> 0 and False
0
>>> False and 0
False
>>> 1 and False
False
>>> False and 1
False
and with True in place of False
>>> 0 and True
0
>>> True and 0
0
>>> 1 and True
True
>>> True and 1
1
Are there any rules when Python convert logical statement to integer? Why does it show sometimes 0 insted of False and 1 insted of True?
What is more, why does it return this?
>>>"test" or "test"
'test'
Nothing is being converted; the Python boolean logic operators instead short circuit.
See the boolean operators documentation:
The expression x and y first evaluates x; if x is false, its value is returned; otherwise, y is evaluated and the resulting value is returned.
The expression x or y first evaluates x; if x is true, its value is returned; otherwise, y is evaluated and the resulting value is returned.
Moreover, numbers that are equal to 0 are considered falsey, as are empty strings and containers. Quoting from the same document:
In the context of Boolean operations, and also when expressions are used by control flow statements, the following values are interpreted as false: False, None, numeric zero of all types, and empty strings and containers (including strings, tuples, lists, dictionaries, sets and frozensets).
Combining these two behaviours means that for 0 and False, the 0 is considered false and returned before evaluating the False expression. For the expression True and 0, True is evaluated and found to be a true value, so 0 is returned. As far as if and while and other boolean operators are concerned, that result, 0 is considered false as well.
You can use this to provide a default value for example:
foo = bar or 'default'
To really convert a non-boolean value into a boolean, use the bool() type; it uses the same rules as boolean expressions to determine the boolean value of the input:
>>> bool(0)
False
>>> bool(0.0)
False
>>> bool([])
False
>>> bool(True and 0)
False
>>> bool(1)
True
To complete the picture, values that are not considered false in a boolean context are instead considered true, including any custom classes. You can change that by implementing a .__nonzero__() special method on your class. If no such method is defined, .__len__() is consulted as well. With either of these methods you can signal that your type is either numeric and should be considered True if non-zero, or it is a container and should be considered True if not empty (has a length over 0).
Python always return one of the operand objects
and returns the first "false" object or the final "true" object.
or returns the first "true" object or the final "false" object.
Note that all 0, 0.0, False, "" are regarded as "false".
>>> "a" and "b" and "c"
'c'
>>> "a" or "b" or "c"
'a'
That's because 0 is s falsy value itself.
>>> bool(0)
False
>>> bool(1)
True
0 and True is 0 because 0 is Falsy ,AND condition stops executing as soon as first falsy value is found and returns that value. If all values were True then the rightmost value is returned.
OR will keep on checking values until first True is not found otherwise it returns the last value.(righmtmost)
From the docs:
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:
None
False
zero of any numeric type, for example, 0, 0L, 0.0, 0j.
any empty sequence, for example, '', (), [].
any empty mapping, for example, {}.
instances of user-defined classes, if the class defines a __nonzero__() or __len__() method, when that method returns the integer zero or bool value False.
All other values are considered true — so objects of many types are always true.
Operations and built-in functions that have a Boolean result always return 0 or False for false and 1 or True for true, unless otherwise stated. (Important exception: the Boolean operations or and and always return one of their operands.)

Categories

Resources