Reputation: 27
I am a new learner in programming, and I was trying to check whether a word exists in a file. I put the words into a dictionary (the exercise told me to put it in a dict as a key and ignore the value), and then let the user input a word, then I tried to check whether it is in the dictionary.
words_dict = dict()
fhand = open('desktop/codingMaterial/words.txt')
for line in fhand:
line = line.rstrip()
t = line.split()
for word in t:
words_dict[word] = 0
# check if word exists
v = input('Enter a variable: ')
if v in words_dict == True:
print('Exist.')
else:
print('Does not exist.')
I tried to run this, but no matter which word I input, the output was always 'Does not exist.'. I think I did something wrong in the if statement, but I am not sure how to fix it.
Upvotes: 0
Views: 935
Reputation: 20689
if v in word_dict == True
is evluated as if v in word_dict and word_dict==True
. This is called operator chaining.
Check this byte-code using dis module.
import dis
a={'a':1,'b':2,'c':3}
In [53]: dis.dis('"a" in a == True')
1 0 LOAD_CONST 0 ('a')
2 LOAD_NAME 0 (a)
4 DUP_TOP
6 ROT_THREE
8 COMPARE_OP 6 (in)
10 JUMP_IF_FALSE_OR_POP 18
12 LOAD_CONST 1 (True)
14 COMPARE_OP 2 (==)
16 RETURN_VALUE
>> 18 ROT_TWO
20 POP_TOP
22 RETURN_VALUE
In [54]: dis.dis('"a" in a and a==True')
1 0 LOAD_CONST 0 ('a')
2 LOAD_NAME 0 (a)
4 COMPARE_OP 6 (in)
6 JUMP_IF_FALSE_OR_POP 14
8 LOAD_NAME 0 (a)
10 LOAD_CONST 1 (True)
12 COMPARE_OP 2 (==)
>> 14 RETURN_VALUE
Both are evaluated in the same way. And in your case word_dict==True
is always False
as word_dict
is a dictionary. So, it would never enter the if
block and else
block is executed.
if some_bool_expr == True
and can be written as if some_bool_expr
and if some_bool_expr==False
can be written as if not some_bool_expr
.
Byte-code Instructions documentation link
LOAD_CONST
and LOAD_NAME
: push value onto the stack. After line 2 top of the stack is a
(not 'a'
)
DUP_TOP
: Duplicates the reference on top of the stack and pushes onto the stack. Now top of the stack is a
.
ROT_THREE
: Lifts second and third stack item one position up moves top down to position three. Now TOS
(top of the stack) is third element (a
) and 2nd element (a
) is now TOS
, 3rd element 'a'
is now 2nd element.
COMPARE_OP
: Tells the interpreter to pop the two topmost stack elements and perform an membership test(in
) between them, pushing the Boolean result back onto the stack. 'a' in a
is done and result is pushed onto the stack i.e True
. Now stack has TOS as True
and duplicate reference from DUP_TOP
below it.
JUMP_IF_FALSE_OR_POP
: If TOS is false, sets the bytecode counter to target and leaves TOS on the stack. Otherwise (TOS is true), TOS is popped. In our example, TOS
is True
so. TOS
is popped. Now TOS
is a
.
LOAD_CONST
True
is pushed onto the stack. COMPARE_OP
==
. True==a
is done which False
. Now TOS
is False
.
RETURN_VALUE
: Returns with TOS to the caller of the function. In our example, TOS
is False
at this point. False
is returned.
POP_TOP
: Removes the top-of-stack (TOS) item.
The only difference between both the expressions is that a
is evaluate twice in the 2nd one.
Also refer to this answer: https://stackoverflow.com/a/3299724/12416453
Upvotes: 3
Reputation: 5405
Verifying that the Python interpreter evaluates the subexpression words_dict == True
first. And not v in words_dict
(wrong assumption has been made). I see from other posts that this statement is not accurate about what the interpreter actually does. Which is pretty interesting. From other descriptions here, it appears the interp regards the in
operator as being at the same level of precedence as ==
. In either case, you could group the first expression to force the order of evaluation.
>>> words_dict == True
False
>>> 'hand' in words_dict == True
False
>>> 'hand' in words_dict
True
>>> ('hand' in words_dict) == True
True
If this were an example of production code, one could implement this logic as (a in b) == c
if that was the intent of the expression. Or if the expression were truly intended to be a in b == c
as discussed, then it could be implemented explicitly as (a in b) and (b == c)
.
In many coding style specifications I've seen, there's a requirement that developers use parenthesis to group their subexpressions in boolean expressions. Whether or not the author believes he has a solid grasp of order-of-precedence (without parenthesis), other developers appreciate the diligence and no-assumptions approach of using parenthesis, not only as a safeguard to ensure there aren't any incorrect assumptions, but to show the other developers the intended order of precedence with no ambiguity. This is a good case-in-point.
There are tables showing the order of precedence of operators online that list in
below ==
. However, this one is directly from docs.python.org and accurately shows that in
and ==
are at the same level: https://docs.python.org/3/reference/expressions.html#operator-precedence
Upvotes: 1
Reputation: 27660
A test of how x in y == z
gets evaluated, by using wrappers that print what's going on:
class C:
def __init__(self, value):
self.value = value
def __eq__(self, other):
print(self, '==', other)
return True
def __contains__(self, value):
print(value, 'in', self)
return True
def __str__(self):
return str(self.value)
C(1) in C(2) == C(3)
Prints 1 in 2
followed by 2 == 3
, as it should.
Not 1 in 2
followed by True == 3
.
Not 2 == 3
followed by 1 in True
.
Upvotes: 3
Reputation: 17322
change if v in words_dict == True:
with if v in words_dict:
your issue is related to Chaining comparison operators, both operators in
and ==
have the same precedence and are evaluated left-to-right, an equivalent to your code is (already pointed by @Ch3steR):
if v in word_dict and word_dict==True
Upvotes: 1
Reputation: 325
Remove the == True.
It should be just .
if v in words_dict:
print('Exist.')
Upvotes: 2