0

the question is in the title. I have a dict of keys and each key value is True or False And I have a list of items. I want to iterate through the dict, and check if (key in list) == (or is) dict[key]

Which means I want to see if there is a match between the return value I will get from the "in" call and the value in the dict,

for example:

quick_dict = dict()
quick_list = list()
quick_dict['hi'] = True
quick_dict["hello"] = True
quick_dict["bye"] = False
quick_dict["bi"] = False
quick_dict['zi'] = True
quick_dict["zv"] = True


quick_list.append("hi")
quick_list.append("bye")
for key in quick_dict:
    if (key in quick_list) == quick_dict[key]:
        print(key)

Which one should I use in this case? and in general what's the different in this case?

elii236
  • 98
  • 4
  • 1
    Does this answer your question? [Boolean identity == True vs is True](https://stackoverflow.com/questions/27276610/boolean-identity-true-vs-is-true) – Ismael Padilla Jan 11 '20 at 20:04
  • 1
    Not really, in this case he checks what return value he get from foo() and check if it's true or false, in my case I want to compare 2 values – elii236 Jan 11 '20 at 20:08
  • @elii236 `(key in quick_list)` evaluates to a boolean: thus it is either `bool is/== bool` or `bool is/== non_bool`. The linked questions explains the differences between the `==` and `is` operators. Regardless, I would use `==` to show *semantic value-equality intentions*. – user2864740 Jan 11 '20 at 20:32
  • Does this answer your question? [Understanding Python's "is" operator](https://stackoverflow.com/questions/13650293/understanding-pythons-is-operator) – Daniel Jan 11 '20 at 21:12
  • Does this answer your question? [Python booleans - if x:, vs if x == True, vs if x is True](https://stackoverflow.com/questions/20420934/python-booleans-if-x-vs-if-x-true-vs-if-x-is-true) – mkrieger1 Jan 11 '20 at 22:56
  • After reading again it seems like you want to use `and`, although I'm not sure what you are trying to achieve. – mkrieger1 Jan 11 '20 at 22:58

3 Answers3

1

In general, you don't want to test boolean variables with is or ==. Just say if ... or put it in a boolean expression by itself.

You want to test 2 conditions, it seems:

  1. Is the key in both collections
  2. Is the dict[key] True

So, you should just write

if key in quick_list and quick_dict[key]:
    # do something

If these lists or dictionaries are "large" you should just use set notation and take then iterate only over the intersection of the 2 sets, which automatically takes care of the first condition and shortens the loop to the intersection of the 2 collections like:

In [4]: quick_set = {1, 3, 5}                                                   

In [5]: quick_dict = {1: True, 2: True, 3: False, 4: True}                      

In [6]: matched_keys = quick_set.intersection(set(quick_dict.keys()))           

In [7]: for k in matched_keys: 
   ...:     if quick_dict[k] : print(k, quick_dict[k]) 
   ...:                                                                         
1 True
AirSquid
  • 10,214
  • 2
  • 7
  • 31
0

Let's look at the code below:

x, y = True, True
u, v = [True], [True]

print(x is y)
print(u is v)

>>> True
>>> False

When using is, there is an added layer of complexity because you're dealing with how the variables are written into memory. We can see that True is stored in memory once. So x and y are hitting the same piece of memory when we call the variable.

Conversely, when we create lists, Python allocates two different places in the memory, one for each list. This means that after the sample code is run, we have True, [True], and [True] stored.

In your example, True and False are written in memory once, no matter how many times we assign them to a variable.

Variables:      x  y     u     v
                 \/      |     |
Memory:         True  [True] [True]
Petar Luketina
  • 449
  • 6
  • 18
0

In Python the True and False (and None too) are called singleton objects. They exist in the process once and only once. Any assignment to these value will always be assigned to the same object. So this means any variable that are assigned to True ARE the same version of True (because there is only one version).

x = True
y = True
x is y
# True
(x is y) is True
# True

Generally, you don't use the either syntax in your question. If you want to check the if a value is True, you just pass it as is:

x = True
if x:
    print('hello world')

This is cleaner, simpler, and easier to read than:

x = True
if x == True:
    print('i am a computer')

Because you are adding an additional evaluation that does not need to take place. In the above example, Python evaluate x == True to True, then evaluates if True to continue the if block.

Except....

The one exception I have seen is sometime you want code to accept either a string or a boolean value, and make a decision based on what is passed. Matplotlib has a bit of this. In this case you might use x is True.

Here is an example of a version number loosener that accepts string or bools.

def format_version(version, how):
    allowed = ('full', 'major', 'minor', 'none', 'true', 'false', True, False)
    if how not in allowed
        raise ValueError(
            "Argument `how` only accepts the following values: {}".format(allowed)
        )

    n = version.count('.')
    if (n == 0) or (how=='full') or (how is True):
        return version
    if n == 1:
        major, minor = version.split('.')
        subs = ''
    if version.count('.') >= 2:
        major, minor, subs = version.split('.', 2)

    if how == 'major':
        return major + '.*'
    if how == 'minor':
        if not subs:
            return '{0}.{1}'.format(major, minor)
        return '{0}.{1}.*'.format(major, minor)

Here are specifically checking if True is passed because a simple boolean check on a string will be true as well. To be clear, this is not a good pattern, it just happens because you want your user to be able to pass a True/False value.

A better way of handling this is to convert to string, and then check the strings. Like this:

def format_version(version, how):
    how = str(how).lower()
    allowed = ('full', 'major', 'minor', 'none', 'true', 'false')
    if how not in allowed
        raise ValueError(
            "Argument `how` only accepts the following values: {}".format(allowed)
        )

    n = version.count('.')
    if (n == 0) or (how == 'full') or (how == 'true'):
        return version
    if n == 1:
        major, minor = version.split('.')
        subs = ''
    if version.count('.') >= 2:
        major, minor, subs = version.split('.', 2)

    if how == 'major':
        return major + '.*'
    if how == 'minor':
        if not subs:
            return '{0}.{1}'.format(major, minor)
        return '{0}.{1}.*'.format(major, minor)
James
  • 32,991
  • 4
  • 47
  • 70