Python 如何比较包含numpy.ndarray(bool(a==b)的数据类的相等性引发ValueError)?
如果我创建了一个包含Numpy ndarray的Python数据类,我就不能再使用自动生成的Python 如何比较包含numpy.ndarray(bool(a==b)的数据类的相等性引发ValueError)?,python,numpy,python-dataclasses,Python,Numpy,Python Dataclasses,如果我创建了一个包含Numpy ndarray的Python数据类,我就不能再使用自动生成的\uuuueq\uuuuu import numpy as np @dataclass class Instr: foo: np.ndarray bar: np.ndarray arr = np.array([1]) arr2 = np.array([1, 2]) print(Instr(arr, arr) == Instr(arr2, arr2)) ValueError:包含多个元
\uuuueq\uuuuu
import numpy as np
@dataclass
class Instr:
foo: np.ndarray
bar: np.ndarray
arr = np.array([1])
arr2 = np.array([1, 2])
print(Instr(arr, arr) == Instr(arr2, arr2))
ValueError:包含多个元素的数组的真值不明确。使用a.any()或a.all()
这是因为ndarray.\uuu eq\uuuu
有时会通过比较a[0]
到b[0]
来返回一个ndarray
真值,依此类推到2中的较长值。这是非常复杂和不直观的,事实上,只有当数组是不同的形状,或具有不同的值或其他东西时才会引发错误
如何安全地比较持有Numpy数组的@dataclass
es
@dataclass
的\uuuuu eq\uuuu
实现是使用eval()
生成的。stacktrace中缺少它的源代码,无法使用inspect
查看,但实际上它使用的是元组比较,它调用bool(foo)
摘录:
解决方案是放入您自己的
\uuuuuueq\uuuuu
方法并设置eq=False
,这样数据类就不会生成自己的数据类(尽管检查最后一步是没有必要的,但我认为还是显式的好)
编辑 通用数据类的通用快速解决方案,其中一些值是numpy数组,而另一些值不是
import numpy as np
from dataclasses import dataclass, astuple
def array_safe_eq(a, b) -> bool:
"""Check if a and b are equal, even if they are numpy arrays"""
if a is b:
return True
if isinstance(a, np.ndarray) and isinstance(b, np.ndarray):
return a.shape == b.shape and (a == b).all()
try:
return a == b
except TypeError:
return NotImplemented
def dc_eq(dc1, dc2) -> bool:
"""checks if two dataclasses which hold numpy arrays are equal"""
if dc1 is dc2:
return True
if dc1.__class__ is not dc2.__class__:
return NotImplmeneted # better than False
t1 = astuple(dc1)
t2 = astuple(dc2)
return all(array_safe_eq(a1, a2) for a1, a2 in zip(t1, t2))
# usage
@dataclass(eq=False)
class T:
a: int
b: np.ndarray
c: np.ndarray
def __eq__(self, other):
return dc_eq(self, other)
您可以在
Instr
上编写自己的\uuuu eq\uuu
方法,您可以覆盖任何自动生成的方法。只需捕获ValueError
并实现您自己的附加逻辑。作为记录,数据类\uuuu eq\uuuu
源代码就在这里这是一点工作,但我想我必须这样做。请记住,在attrs/数据类之前,您必须这样做,我最终比较了arr.tolist()
所以我不必担心由于广播诡计而导致数组错误相等,这是一个好主意吗?这会起作用,但效率不高。我们在这里谈论的是什么广播把戏?这主要是为了单元测试,我不想要1==[1]=[[1]]]或[1,1,1]==1。
3 12 LOAD_FAST 0 (self)
14 LOAD_ATTR 1 (foo)
16 LOAD_FAST 0 (self)
18 LOAD_ATTR 2 (bar)
20 BUILD_TUPLE 2
22 LOAD_FAST 1 (other)
24 LOAD_ATTR 1 (foo)
26 LOAD_FAST 1 (other)
28 LOAD_ATTR 2 (bar)
30 BUILD_TUPLE 2
32 COMPARE_OP 2 (==)
34 RETURN_VALUE
import numpy as np
def array_eq(arr1, arr2):
return (isinstance(arr1, np.ndarray) and
isinstance(arr2, np.ndarray) and
arr1.shape == arr2.shape and
(arr1 == arr2).all())
@dataclass(eq=False)
class Instr:
foo: np.ndarray
bar: np.ndarray
def __eq__(self, other):
if not isinstance(other, Instr):
return NotImplemented
return array_eq(self.foo, other.foo) and array_eq(self.bar, other.bar)
import numpy as np
from dataclasses import dataclass, astuple
def array_safe_eq(a, b) -> bool:
"""Check if a and b are equal, even if they are numpy arrays"""
if a is b:
return True
if isinstance(a, np.ndarray) and isinstance(b, np.ndarray):
return a.shape == b.shape and (a == b).all()
try:
return a == b
except TypeError:
return NotImplemented
def dc_eq(dc1, dc2) -> bool:
"""checks if two dataclasses which hold numpy arrays are equal"""
if dc1 is dc2:
return True
if dc1.__class__ is not dc2.__class__:
return NotImplmeneted # better than False
t1 = astuple(dc1)
t2 = astuple(dc2)
return all(array_safe_eq(a1, a2) for a1, a2 in zip(t1, t2))
# usage
@dataclass(eq=False)
class T:
a: int
b: np.ndarray
c: np.ndarray
def __eq__(self, other):
return dc_eq(self, other)