Cannot test python list element membership when element contains numpy array

Viewed 89

I defined a custom data structure using namedtuple, which contains numpy array.

from collections import namedtuple
import numpy as np

AA = namedtuple('AA', 'name, x')
c = []
c.append(AA('x', np.arange(3)))
c.append(AA('x', np.arange(3)))
c.append(AA('y', np.arange(3)))
c[0] in c
c[1] in c
c[2] in c

Strangely, the c[1] test fails, whereas the other two works.

ValueError                                Traceback (most recent call last)
<ipython-input-70-c1daf83cd082> in <module>
----> 1 c[1] in c

ValueError: The truth value of an array with more than one element is ambiguous. Use c.any() or c.all()

The error seems to be related to numpy arrray equality test c[0].x == c[1].x. But for some reason the membership test for the first element always succeeds

s = []
s.append(np.arange(3))
s.append(np.arange(3))
s[0] in s
s[1] in s

Also see this example

class A:
    def __eq__(self, other):
        raise ValueError
        
a = [A(), A()]
a[0] in a
a[1] in a

Also I don't know why c[2] in c returns True.

1 Answers

Simplified:

def my_in(obj, container):
    for elem in container:
        if bool(my_compare_equal(obj, elem)):
            return True
    return False

def my_compare_equal(obj, elem):
    if id(obj) == id(elem):
        return True
    return obj.__eq__(elem)

For more datails, see list_contains and then PyObject_RichCompareBool, PyObject_RichCompare, do_richcompare

"Pseudo-execution" steps:

c = [AA('x', np.arange(3)),
     AA('x', np.arange(3)),
     AA('y', np.arange(3))]

c[0] in c
# explain:
my_in(obj=c[0], container=c)
    # for loop:
    
    # 0-iteration:
    # elem = c[0]
    my_compare_equal(obj=c[0], elem=c[0])
        # (id(c[0]) == id(c[0])) == True
        # --> True
    bool(True)
        # True.__bool__()
        # --> True
    # --> True

c[1] in c
# explain:
my_in(obj=c[1], container=c)
    # for loop:

    # 0-iteration:
    # elem = c[0]
    my_compare_equal(obj=c[1], elem=c[0])
        # (id(c[1]) == id(c[0])) == False
        # c[1].__eq__(c[0])
            # compare tuples element by element:
            # 0-iteration:
            my_compare_equal('x', 'x') == True
            # 1-iteration:
            my_compare_equal(np.arange(3), np.arange(3))
                # (id(np.arange(3)) == id(np.arange(3))) == False
                # np.arange(3).__eq__(np.arange(3))
                # --> np.ndarray([True, True, True])
            bool(np.ndarray([True, True, True]))
                # np.ndarray([True, True, True]).__bool__()
                    raise ValueError("The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()")

c[2] in c
# explain:
my_in(obj=c[2], container=c)
    # for loop:

    # 0-iteration:
    # elem = c[0]
    my_compare_equal(obj=c[2], elem=c[0])
        # (id(c[2]) == id(c[0])) == False
        # c[2].__eq__(c[0])
            # compare tuples element by element:
            # 0-iteration:
            my_compare_equal('y', 'x') == False
            # --> False
        # --> False

    # 1-iteration:
    # analogiusly as 0-iteration:
    my_compare_equal(obj=c[2], elem=c[1])
        # --> False
    
    # 2-iteration:
    my_compare_equal(obj=c[2], elem=c[2])
        # (id(c[2]) == id(c[2])) == True
        # --> True
    # --> True
Related