0

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.

Ch3steR
  • 20,090
  • 4
  • 28
  • 58
May Ding
  • 27
  • 3
  • 1) print the dictionary before you check it 2) try `if (v in words_dict) == True:` – OneCricketeer Feb 22 '20 at 16:52
  • Also, use a set for checking existence, not a dict – OneCricketeer Feb 22 '20 at 16:54
  • ``if (v in words_dict) == True: `` or ``if v in words_dict:`` – jizhihaoSAMA Feb 22 '20 at 16:54
  • @cricket_007, why do you give a recommendation without reading question? – Olvin Roght Feb 22 '20 at 16:55
  • @Olvin because teachers/exercises often are wrong – OneCricketeer Feb 22 '20 at 16:56
  • @cricket_007 Thank you for your suggestion. That is a exercise in the Dictionary chapter, I think that's the reason the exercise told me to do so. Anyway thanks so much :) – May Ding Feb 22 '20 at 17:02
  • @Todd I tried to use a word in that file and the result is 'Does not exists.' – May Ding Feb 22 '20 at 17:11
  • @Todd Yeah I have added it. And the words_dict has exactly what I want in it – May Ding Feb 22 '20 at 17:17
  • @cricket_007, the idea of exercise is to follow it's subject. And, btw, being self-confident is good, but better to avoid blaming other people without knowing context. – Olvin Roght Feb 22 '20 at 17:18
  • @MayDing also print the text you got from user input so you can verify that too visually: `print("%s exists." % v)` and `print("%s doesn't exist." % v)` – Todd Feb 22 '20 at 17:28
  • @Todd Thank you for your suggestion, I thinks it's really helpful – May Ding Feb 22 '20 at 17:31
  • 1
    Looks like @cricket_007 is a little unpopular on this thread, but I do understand his suggestion to use a `set`. And I do think it's a good suggestion @MayDing. With a `set` you don't get duplicate strings in your test data. However, this is a school assignment for `dict` objects. – Todd Feb 23 '20 at 00:03

5 Answers5

3

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

Ch3steR
  • 20,090
  • 4
  • 28
  • 58
  • 1
    I wouldn't say "same way", as `dis` does show differences and because `a` is evaluated *twice* in the long version (which in general can make a difference, as `a` could've changed between the two evaluations). – Kelly Bundy Feb 22 '20 at 17:47
  • This is a good demo. How exactly do you get this output? Nevermind, I figured it out. – Todd Feb 22 '20 at 18:38
  • This is a good demo for experienced Pythoners. Maybe a little explanation on the effect of dup and rot would help. – Todd Feb 22 '20 at 18:46
  • @Todd Yeah, I should've at least left a link to `dis` module documentation. – Ch3steR Feb 22 '20 at 20:10
  • @Ch3steR, you have, "perform an inequality comparison" in your explanation. I think you meant an "'in' membership test operation". – Todd Feb 22 '20 at 23:51
  • Thanks so much for your answer. Though I can not completely understand the byte-code part cause I'm a beginner, your answer solves my question. I'll investigate on that part later. Thanks again :) – May Ding Feb 23 '20 at 08:35
3

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.

Kelly Bundy
  • 23,480
  • 7
  • 29
  • 65
  • Nice example to show operator chaining. Heap can you tell the differences in the examples I provided. Python docs says *expr1 opr1 expr2 opr2 expr3* is evaluated as *expr1 opr1 expr2 and expr2 opr2 expr3* right? – Ch3steR Feb 22 '20 at 19:21
  • @Ch3steR In your particular example, the chain version and the `and` version have the same meaning, they're just executed a bit differently and probably have a speed difference so small it's hard to measure. In general, they *don't* mean the same thing. You're only showing the first half of the documentation's sentence. The second half is "except that [expr2] is evaluated only once". A typical counter-example is when `expr2` is an expression resulting in a different value when it's evaluated a second time. If it's just a variable, I need to get dirty: https://repl.it/repls/UnusualWhoppingMap – Kelly Bundy Feb 22 '20 at 20:30
  • Yes, got your point. And I went through the docs. And `dis` documentation made it clear. Nice example btw ;) – Ch3steR Feb 22 '20 at 20:35
2

Remove the == True.
It should be just .

if v in words_dict:
    print('Exist.')
Akhil Nambiar
  • 315
  • 3
  • 18
1

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
kederrac
  • 16,819
  • 6
  • 32
  • 55
1

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

Todd
  • 4,669
  • 1
  • 22
  • 30
  • See the comments under kederrac's answer. – Kelly Bundy Feb 22 '20 at 17:50
  • Not sure why you mention points. What I mean is that your demo is wrong. – Kelly Bundy Feb 22 '20 at 17:55
  • Well.. the demo's not "wrong", the commands and their results are accurately displayed. But the assumption that `==` takes precedence over `in` was wrong. From other examples, it looks as if they are at the same order of precedence. – Todd Feb 22 '20 at 18:53
  • How would you use parenthesis in `a in b == c` *without* changing the meaning? – Stefan Pochmann Feb 22 '20 at 19:36
  • I **was** trying to change the meaning ;-) Adding parenthesis ensures that the logic is evaluated in the order I **intend**. – Todd Feb 22 '20 at 19:42
  • In this case we want to change it, yes. But I mean your paragraph about coding style in general, when I don't want to change the meaning. – Stefan Pochmann Feb 22 '20 at 19:59
  • Oh. got it! I'd do it this way: `(a in b) and (b == c)`. I've updated it @StefanPochmann – Todd Feb 22 '20 at 22:23