与 NumPy 实例调用 `__bool__` 的相等比较
Equality Comparison with NumPy Instance Invokes `__bool__`
我定义了一个class,其中它的__ge__
方法returns是它自己的一个实例,并且不允许调用它的__bool__
方法(类似于Pandas Series
).
为什么 X.__bool__
在 np.int8(0) <= x
期间被调用,而其他任何示例都没有? 谁在调用它?我已经阅读了 Data Model 文档,但我没有在那里找到我的答案。
import numpy as np
import pandas as pd
class X:
def __bool__(self):
print(f"{self}.__bool__")
assert False
def __ge__(self, other):
print(f"{self}.__ge__")
return X()
x = X()
np.int8(0) <= x
# Console output:
# <__main__.X object at 0x000001BAC70D5C70>.__ge__
# <__main__.X object at 0x000001BAC70D5D90>.__bool__
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# File "<stdin>", line 4, in __bool__
# AssertionError
0 <= x
# Console output:
# <__main__.X object at 0x000001BAC70D5C70>.__ge__
# <__main__.X object at 0x000001BAC70D5DF0>
x >= np.int8(0)
# Console output:
# <__main__.X object at 0x000001BAC70D5C70>.__ge__
# <__main__.X object at 0x000001BAC70D5D30>
pd_ge = pd.Series.__ge__
def ge_wrapper(self, other):
print("pd.Series.__ge__")
return pd_ge(self, other)
pd.Series.__ge__ = ge_wrapper
pd_bool = pd.Series.__bool__
def bool_wrapper(self):
print("pd.Series.__bool__")
return pd_bool(self)
pd.Series.__bool__ = bool_wrapper
np.int8(0) <= pd.Series([1,2,3])
# Console output:
# pd.Series.__ge__
# 0 True
# 1 True
# 2 True
# dtype: bool
我怀疑 np.int8.__le__
的定义不是 returning NotImplemented
并让 X.__ge__
接管,而是尝试 return 一些东西像 not (np.int(8) > x)
,然后 然后 np.int8.__gt__
加注 NotImplemented
。一旦 X.__gt__(x, np.int8(0))
return 是 X
的实例而不是布尔值,那么我们需要调用 x.__bool__()
来计算 not x
的值。
(仍在尝试追踪定义 int8.__gt__
的位置以确认。)
(更新:不完全是。int8
使用单个通用的丰富比较函数,该函数简单地将值转换为 0 维数组,然后 returns PyObject_RichCompare
的结果在数组和 x
.)
我确实发现 this function 似乎最终实现了 np.int8.__le__
:
static NPY_INLINE int
rational_le(rational x, rational y) {
return !rational_lt(y,x);
}
我不清楚如果其中一个参数(如 X
)不是 NumPy 类型,我们如何避免使用此函数。我想我放弃了。
TL;DR
X.__array_priority__ = 1000
最大的提示是它适用于 pd.Series
。
首先,我尝试让 X
继承自 pd.Series
。这有效(即 __bool__
不再调用)。
为了确定 NumPy 是否使用 isinstance
检查或鸭子类型方法,我删除了显式继承并添加(基于 this answer):
@property
def __class__(self):
return pd.Series
操作不再有效(即调用了 __bool__
)。
所以现在我认为我们可以得出结论,NumPy 正在使用鸭子类型的方法。所以我查看了 X
.
上访问了哪些属性
我在 X
中添加了以下内容:
def __getattribute__(self, item):
print("getattr", item)
return object.__getattribute__(self, item)
再次将 X
实例化为 x
,并调用 np.int8(0) <= x
,我们得到:
getattr __array_priority__
getattr __array_priority__
getattr __array_priority__
getattr __array_struct__
getattr __array_interface__
getattr __array__
getattr __array_prepare__
<__main__.X object at 0x000002022AB5DBE0>.__ge__
<__main__.X object at 0x000002021A73BE50>.__bool__
getattr __array_struct__
getattr __array_interface__
getattr __array__
Traceback (most recent call last):
File "<stdin>", line 32, in <module>
np.int8(0) <= x
File "<stdin>", line 21, in __bool__
assert False
AssertionError
啊哈!什么是 __array_priority__
?谁在乎,真的。稍加挖掘,我们只需要知道 NDFrame
(pd.Series
继承)将此值设置为 1000
.
如果我们添加 X.__array_priority__ = 1000
,就可以了! __bool__
不再被调用。
之所以如此困难(我相信)是因为 NumPy 代码没有出现在调用堆栈中,因为它是用 C 编写的。如果我尝试了建议,我可以进一步调查 .
我定义了一个class,其中它的__ge__
方法returns是它自己的一个实例,并且不允许调用它的__bool__
方法(类似于Pandas Series
).
为什么 X.__bool__
在 np.int8(0) <= x
期间被调用,而其他任何示例都没有? 谁在调用它?我已经阅读了 Data Model 文档,但我没有在那里找到我的答案。
import numpy as np
import pandas as pd
class X:
def __bool__(self):
print(f"{self}.__bool__")
assert False
def __ge__(self, other):
print(f"{self}.__ge__")
return X()
x = X()
np.int8(0) <= x
# Console output:
# <__main__.X object at 0x000001BAC70D5C70>.__ge__
# <__main__.X object at 0x000001BAC70D5D90>.__bool__
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# File "<stdin>", line 4, in __bool__
# AssertionError
0 <= x
# Console output:
# <__main__.X object at 0x000001BAC70D5C70>.__ge__
# <__main__.X object at 0x000001BAC70D5DF0>
x >= np.int8(0)
# Console output:
# <__main__.X object at 0x000001BAC70D5C70>.__ge__
# <__main__.X object at 0x000001BAC70D5D30>
pd_ge = pd.Series.__ge__
def ge_wrapper(self, other):
print("pd.Series.__ge__")
return pd_ge(self, other)
pd.Series.__ge__ = ge_wrapper
pd_bool = pd.Series.__bool__
def bool_wrapper(self):
print("pd.Series.__bool__")
return pd_bool(self)
pd.Series.__bool__ = bool_wrapper
np.int8(0) <= pd.Series([1,2,3])
# Console output:
# pd.Series.__ge__
# 0 True
# 1 True
# 2 True
# dtype: bool
我怀疑 np.int8.__le__
的定义不是 returning NotImplemented
并让 X.__ge__
接管,而是尝试 return 一些东西像 not (np.int(8) > x)
,然后 然后 np.int8.__gt__
加注 NotImplemented
。一旦 X.__gt__(x, np.int8(0))
return 是 X
的实例而不是布尔值,那么我们需要调用 x.__bool__()
来计算 not x
的值。
(仍在尝试追踪定义 int8.__gt__
的位置以确认。)
(更新:不完全是。int8
使用单个通用的丰富比较函数,该函数简单地将值转换为 0 维数组,然后 returns PyObject_RichCompare
的结果在数组和 x
.)
我确实发现 this function 似乎最终实现了 np.int8.__le__
:
static NPY_INLINE int
rational_le(rational x, rational y) {
return !rational_lt(y,x);
}
我不清楚如果其中一个参数(如 X
)不是 NumPy 类型,我们如何避免使用此函数。我想我放弃了。
TL;DR
X.__array_priority__ = 1000
最大的提示是它适用于 pd.Series
。
首先,我尝试让 X
继承自 pd.Series
。这有效(即 __bool__
不再调用)。
为了确定 NumPy 是否使用 isinstance
检查或鸭子类型方法,我删除了显式继承并添加(基于 this answer):
@property
def __class__(self):
return pd.Series
操作不再有效(即调用了 __bool__
)。
所以现在我认为我们可以得出结论,NumPy 正在使用鸭子类型的方法。所以我查看了 X
.
我在 X
中添加了以下内容:
def __getattribute__(self, item):
print("getattr", item)
return object.__getattribute__(self, item)
再次将 X
实例化为 x
,并调用 np.int8(0) <= x
,我们得到:
getattr __array_priority__
getattr __array_priority__
getattr __array_priority__
getattr __array_struct__
getattr __array_interface__
getattr __array__
getattr __array_prepare__
<__main__.X object at 0x000002022AB5DBE0>.__ge__
<__main__.X object at 0x000002021A73BE50>.__bool__
getattr __array_struct__
getattr __array_interface__
getattr __array__
Traceback (most recent call last):
File "<stdin>", line 32, in <module>
np.int8(0) <= x
File "<stdin>", line 21, in __bool__
assert False
AssertionError
啊哈!什么是 __array_priority__
?谁在乎,真的。稍加挖掘,我们只需要知道 NDFrame
(pd.Series
继承)将此值设置为 1000
.
如果我们添加 X.__array_priority__ = 1000
,就可以了! __bool__
不再被调用。
之所以如此困难(我相信)是因为 NumPy 代码没有出现在调用堆栈中,因为它是用 C 编写的。如果我尝试了建议,我可以进一步调查