Disjunction and membership in python [duplicate] - python

This question already has answers here:
Why does "a == x or y or z" always evaluate to True? How can I compare "a" to all of those?
(8 answers)
Closed last year.
When I try this code:
a = ['b']
if 'i' or 'j' in a:
print('Yes!')
else:
print('No!')
The output is 'Yes!'. Flake8 and python do not complain about an error or bad form.
What exactly happens when the code runs? Why does 'i' or 'j' in a evaluate as true?

The problem is that in the statement i evaluates to True, which means that the statement is then: True or 'j' in a. This is always true and your result will be always 'Yes!'.
You can use something like this to check if one of the values is in your list:
a = ['b']
chars_to_check = ['i', 'j']
filtered_list = [i for i in a if i in chars_to_check]
if len(filtered_list)>0:
print('Yes!')
else:
print('No!')
The example is from this question, where people also posted more efficient or shorter solutions to your problem. The solution I like the most would be this one:
a = ['b']
if {'i','j'} & set(a):
print('Yes!')
else:
print('No!')
EDIT: I think now I understand the question.
First of all python sees that you have a or in your if statement. This means the first expression (in your case 'i') is evaluated first. If the first expression is True, the whole statement is True and the second expression is not even evaluated. The evaluation order of or is explained here for example.
Now to why the first expression is always True. Python automatically evaluates all objects not only boolean values. For this the objects can for example contain a function __bool__() that gives back the boolean value of an object. The object in your case is a single character 'i', which evaluates to True. The reason is that it is defined that the boolean value of a string is always True except for the empty string (''). Here you can see an example of the evaluation:
print(bool('i')) # True
print(bool('')) # False
An answer that shows which objects are considered False and which are considered True can you find here.

Related

Python: `and` operator does not return a boolean value

In Python, an empty list is considered a Falsey value
Therefore this is how things should work:
>>> [] and False
False
But in reality, python returns an empty list.
>>> [] and False
[]
Is this intended or a bug?
It's intended. Both and and or are defined to return the last thing evaluated (based on short-circuiting), not actually True or False. For and, this means it returns the first falsy value, if any, and the last value (regardless of truthiness) if all the others are truthy.
It was especially useful back before the conditional expression was added, as it let you do some almost-equivalent hacks, e.g. before the conditional expression:
b if a else c
could be written as:
a and b or c
and, assuming b itself was some truthy thing, it would behave equivalently (the conditional expression lacked that limitation and was more clear about intent, which is why it was added). Even today this feature is occasionally useful for replacing all falsy values with some more specifically-typed default, e.g. when lst might be passed as None or a list, you can ensure it's a list with:
lst = lst or []
to cheaply replace None (and any other falsy thing) with a new empty list.
This is how it is supposed to work. and will only return the right hand operand if the left hand operand is truthy. Since [] is falsy, and returns the left hand operand.
That's a totally expected behaviour. To understand it, you need to know how the Boolean operators (and, or, not) work. From the Boolean Operations 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.
Now let's consider your example: [] and False. Here, since [] is falsey, it's value is returned back by the statement which is [].
Above linked Python documentation explicitly mentions:
Note: Neither and nor or restrict the value and type they return to False and True, but rather return the last evaluated argument.
However, in case you need the return value as boolean, you can explicitly type-cast the value to True or False using the bool() function.
For example, in your case it will return as False:
>>> bool([] and False)
False

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 "or" usage with "in" statement [duplicate]

This question already has answers here:
What is the motivation for the "or" operator to not return a bool?
(6 answers)
Closed 5 years ago.
I've this code:
url='http://mybeautifulurl.com'
('h' or 'f') in url[0:4] #This is True
('f' or 'h') in url[0:4] #This is False
I'm just trying to understand why the 'or' operator seems to evaluate just the first condition.
This or returns the first element if it evaluates to true (as a bool) and the second one otherwise... it's is usually applied to set default values.
This way, 'h' or 'f' is simply 'h' and 'f' or 'h' is simply 'f'.
You can achieve what you want with something like:
any(x in url[:4] for x in ['h', 'f'])

Is OR and ELSE similar in list comprehension statement

Kindly help me understand why this works. The code below lists duplicates in an iterable. However, the use of the or operator behaves like the else in an if..else statement..
j = set()
my_list = [1, 2, 3 ,3 , 3 ,4, 4]
j_add = j.add
twice = set(x for x in my_list if x in j or j_add(x))
print list(twice)
Would expect the line to be:
twice = set(x for x in my_list if x in j else j_add(x))
Thought or returns a boolean not a value
The or operator returns the last evaluated argument, which may or may not be a Boolean.
This behavior is explained in the Documentation:
Note that neither and nor or restrict the value and type they return to False and True, but rather return the last evaluated argument. This is sometimes useful, e.g., if s is a string that should be replaced by a default value if it is empty, the expression s or 'foo' yields the desired value.
Of course, it helps to remember what is interpreted as false and what is interpreted as true:
[T]he 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.
So in the expression:
A = B or C
As #MartijnPieters points out in a comment, an or expression short-circuits. If the first argument (B in this case) is interpreted as true, the entire expression must be true so the second argument (C) is never evaluated. Therefore the first argument (B) is "the last evaluated argument" and is what is returned. However, if the first argument (B) is interpreted as false, the second argument (C) must still be evaluated to determined the truthiness of the expression (no short-circuit takes place). In that case, "the last evaluated argument" is the second argument (C), which is returned regardless of whether the expression evaluates true or false.
It effectively accomplishes the same as the Conditional Expression:
A = B if B else C
However, Conditional Expressions were only added to Python in version 2.5, while the Boolean Operator behavior has existed from the beginning (or at least for a very long time). Most seasoned Python programmers will easily recognize and are in the habit of using A = B or C. Conditional Expressions are commonly reserved for more complex conditions that won't work with a simple or (for example in A = B if X else C the condition is not based on the truthiness of B but X, which could be anything from a simple value to a complex expression).
However, you need to be careful because, as JaredGoguen points out in his answer, changing the or to an else in the OP's sample actually changes the behavior of the code. That code was written to depend on this specific behavior of the or operator. You can't just replace any use of or for assignment with a Conditional Expression. Additional refactoring may be needed as well.
I might make a value judgment here and say that this is not good code because it is using the short-circuiting behavior of or to produce a side-effect.
Consider the given conditional: if x in j or j_add(x).
When x in j, the or short-circuits, skips the j_add(x) part of the conditional, and evaluates as True.
When x not in j, the statement j_add(x) is checked for its truthiness. This method returns None, which is falsy, and so or evaluate as False.
So, the entire conditional will evaluate the same as x in j. However j_add(x) has the side-effect of adding x to j! This side-effect is being exploited in order to record the unique members my_list in a quick-and-dirty comprehension.
Changing the or to an else would still construct j as desired, but it would inappropriately add None, the return value of j_add(x), to twice.

Python - function to do string comparison

def name(x):
return x==('Jenson'or'Amra'or'McCay'or'Spinner'or'Jones')
print(name('Jenson'))
print(name('McCay'))
This is the question:
"Write a function that takes as input a name of a person (e.g.,
“smith”, “jones”, etc.) This function should check to see if the name
is one of the five names of people on the board. The five names are:
“Jenson”,”Amra”, “McCay”,”Spinner”, and “Jones”. If the name input
into the function is one of those five names, the function should
return the Boolean value True, and if it isn’t, the function should
return False. (remember comments with input types, description, and
test cases) Test the function to make sure it works."
It works if I am doing Jenson but it comes out with false if I put in any other name.
Try like this,
def name(x):
return x in ('Jenson', 'Amra' ,'McCay', 'Spinner','Jones')
How about the "long" way:
def check_name(x):
names_to_check = ('Jenson','Amra','McCay','Spinner','Jones')
for i in names_to_check:
if i == x:
return True
return False
Here is what is happening in your code:
x = 'Jenson', since this is what you have passed in.
This line x == ('Jenson' or 'Amra' or 'McCay' or 'Jones') is actually a boolean operation, and the result of it is always Jenson.
Boolean operations check truth values, and a non-empty string in Python is always True. So actually what ('Jenson' or 'Amra' or 'McCay' or 'Jones') is saying is:
"Either Jenson or Amra or McCay or Jones which ever one is True, set the value to that".
Since Jenson is the first item, and its True (that is, its not an empty string), the entire expression is equal to Jenson (which is why it only works when you pass in Jenson).
A simple example:
>>> ('a' or 'b' or 'c')
'a'
>>> ('b' or 'a' or 'c')
'b'
>>> ('' or '' or 'a')
'a'
>>> (0 or 0 or 1)
1
>>> (False or False or True)
True
The last three illustrate the same comparison. I am checking two empty strings and 'a'. Since an empty string is False in Python, the only thing that is "True" is 'a', which is what is returned, just as if I was comparing 0 with 1.
The syntax x==('Jenson' or 'Amra' or 'McCay' or 'Spinner'or'Jones') is wrong.
It should be like Adem says. or maybe
def name(x):
return x=='Jenson' or x== 'Amra' or x == 'McCay' or x == 'Spinner' or x == 'Jones'
I imagine what is happening is that ('Jenson'or'Amra'or'McCay'or'Spinner'or'Jones') is being evaluated first, and is evaluated to 'Jenson'. That is computed before x is even considered because it is in parentheses. Then x is checked for equality against Jenson. You need to either use a more advanced syntax like x in... as in Adem's answer, or else use return x == 'Jenson' or x == 'Amra' or x == 'McCay'... so that each comparison is run one after another.

Categories

Resources