【问题标题】:Cannot test python list element membership when element contains numpy array当元素包含numpy数组时无法测试python列表元素成员资格
【发布时间】:2021-04-26 18:13:20
【问题描述】:

我使用namedtuple定义了一个自定义数据结构,其中包含numpy数组。

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

奇怪的是,c[1] 测试失败了,而其他两个测试成功了。

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()

该错误似乎与 numpy 数组相等性测试 c[0].x == c[1].x 有关。但是由于某种原因,第一个元素的成员资格测试总是成功

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

另见此示例

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

我也不知道为什么c[2] in c返回True

【问题讨论】:

  • c[0] in c 这会问“c 的第一个元素是 c 的成员吗?”,这显然是正确的。这里的目的是什么?
  • @JohnGordon 和c[1] in c 对第二个元素提出了同样的问题,但它失败了。这就是问题所在。奇怪的是,如果你添加了第三个元素的副本,那么项目 1 和 3 会失败,但不会失败 0 和 2。我需要考虑这个。
  • Numpy eq 可能会在比较元素之前检查 iddatashape。因此c[1] in c 失败,s[1] in snp.arange(3) in s 也是如此,因为在这种情况下,比较会降级为逐元素比较。
  • 好的,只有在必须检查数组的情况下才会失败。如果“名称”元素不同,则不必检查数组。
  • 不管怎样,同样的事情发生在一个普通的(未命名的)元组上。

标签: python arrays list numpy


【解决方案1】:

简化:

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)

更多数据请见list_contains,然后是PyObject_RichCompareBoolPyObject_RichComparedo_richcompare强>

“伪执行”步骤:

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

【讨论】:

  • 一句解释代码的散文会大大改善这个答案。我明白你在展示什么,但你实际上并没有以不知道你在做什么(并且不想阅读 C 源代码)的人的方式可以关注。
  • 添加伪执行步骤
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-01
  • 2012-02-16
  • 2012-03-02
  • 1970-01-01
  • 2014-04-28
  • 2022-08-19
  • 1970-01-01
相关资源
最近更新 更多