avnr
avnr

Reputation: 666

Precise Membership Test in Python

The in operator tests for equivalence using comparison, but Python's comparison isn't precise in the sense that True == 1 and 0 == False, yielding -

>>> True in [ 1 ]
True
>>> False in [ 0 ]
True
>>> 1 in [ True ]
True
>>> 0 in [ False ]
True

whereas I need a precise comparison (similar to === in other languages) that would yield False in all of the above examples. I could of course iterate over the list:

res = False
for member in mylist:
    if subject == member and type( subject ) == type( member ):
        res = True
        break

This is obviously much less efficient then using the builtin in operator, even if I pack it as a list comprehension. Is there some native alternative to in such as a list method or some way to tweak in's behavior to get the required result?

The in operator is used in my case for testing the uniqueness of all list members, so a native uniqueness test would do as well.

Important note: The list may contain mutable values, so using set isn't an option.

Python version is 3.4, would be great for the solution to work on 2.7 too.

EDIT TO ALL THOSE WHO SUGGEST USING IS:

  1. I look for a non-iterating, native alternative to a in b.
  2. The is operator is not relevant for this case. For example, in the following situation in works just fine but is won't:

    >>> [1,2] in [[1,2]]
    True
    

Please, do read the question before answering it...

Upvotes: 0

Views: 140

Answers (3)

davidedb
davidedb

Reputation: 876

Python in operator is precise and the behavior you're complaining of is perfectly expected, since bool is a subclass of int.

Below is the excerpt of the official Python documentation describing the boolean type:

Booleans

These represent the truth values False and True. The two objects representing the values False and True are the only Boolean objects. The Boolean type is a subtype of plain integers, and Boolean values behave like the values 0 and 1, respectively, in almost all contexts, the exception being that when converted to a string, the strings "False" or "True" are returned, respectively.

You can also have a look at PEP 285.

Upvotes: 1

Rohcana
Rohcana

Reputation: 359

in doesn't test for equivalence at all. It checks if an item is in a container. Example:

>>> 5 in [1,2,3,4,5]
True
>>> 6 in [1,2,3,4,5]
False
>>> True in {True, False}
True
>>> "k" in ("b","c")
True

What you are looking for is is.

>>> True == 1
True
>>> True is 1
False
>>> False == 0
True
>>> False is 0
False

EDIT

After reading your edit, I don't think there is something built in in python libraries that suits your needs. What you want is basically to differentiate between int and bool (True, False). But python itself treats True and False as integers. This is because bool is a subclass of int. Which is why True == 1 and False==0 evaluates to true. You can even do:

>>> isinstance ( True, int)
True

I cannot think of anything better than your own solution, However, if your list is certain to contain any item not more than once you can use list.index()

try:
    index_val = mylist.index(subject)
except ValueError:
    index_val = None

if (index_val!=None):
    return type(subject) == type(member)

Since index is built-in, it might be a little faster, though rather inelegant.

Upvotes: 3

Blender
Blender

Reputation: 298430

You're looking for the is operator:

if any(x is True for x in l):
    ...

is, however, isn't exactly === from other languages. is checks identity, not just equality without type coercion. Since CPython uses string and integer interning, two objects that are equal may not be the same object:

In [19]: a = '12'

In [20]: b = '123'

In [21]: a += '3'

In [22]: a is b
Out[22]: False

In [23]: a == b
Out[23]: True

In [27]: 100001 is 100000 + 1
Out[27]: False

In [28]: 100001 == 100000 + 1
Out[28]: True

In Python 3, None, True, and False are essentially singletons, so using is for discerning True from 1 will work perfectly fine. In Python 2, however, this is possible:

In [29]: True = 1

In [31]: True is 1
Out[31]: True

Equality can be overridden __eq__ method, so you can define an object that is equal to any other object:

In [1]: %paste
    class Test(object):
        def __eq__(self, other):
            return True

## -- End pasted text --

In [2]: x = Test()

In [3]: x == None
Out[3]: True

In [4]: x == True
Out[4]: True

In [5]: x == False
Out[5]: True

In this case, how would === work? There is no general solution, so Python has no built-in method of lists that does what you want.

Upvotes: 1

Related Questions