与 NumPy 实例调用 `__bool__` 的相等性比较

Mik*_*e R 5 python numpy magic-methods pandas

我定义了一个类,它的__ge__方法返回自身的一个实例,并且__bool__不允许调用其方法(类似于 Pandas Series)。

为什么X.__bool__在 during 中被调用np.int8(0) <= x,而不是在其他任何示例中被调用?谁在调用它?我已经阅读了数据模型文档,但我还没有在那里找到答案。

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
Run Code Online (Sandbox Code Playgroud)

Mik*_*e R 0

长话短说

X.__array_priority__ = 1000


最大的提示是它可以与pd.Series.

首先我尝试X继承自pd.Series. 这有效(即__bool__ 不再调用)。

为了确定 NumPy 是否使用isinstance检查或鸭子类型方法,我删除了显式继承并添加了(基于此答案):

@property
def __class__(self):
    return pd.Series
Run Code Online (Sandbox Code Playgroud)

该操作不再起作用(即被__bool__调用)。

所以现在我认为我们可以得出结论 NumPy 正在使用鸭子类型方法。因此我检查了哪些属性正在被访问X

我添加了以下内容X

def __getattribute__(self, item):
    print("getattr", item)
    return object.__getattribute__(self, item)
Run Code Online (Sandbox Code Playgroud)

再次实例化为Xx调用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
Run Code Online (Sandbox Code Playgroud)

啊哈!什么是__array_priority__?谁在乎呢,真的。经过一点挖掘,我们需要知道的是NDFrame(从中pd.Series继承)将此值设置为1000

如果我们添加X.__array_priority__ = 1000,它就会起作用!__bool__不再被称为。

是什么让这变得如此困难(我相信)是 NumPy 代码没有出现在调用堆栈中,因为它是用 C 编写的。如果我尝试了此处的建议,我可以进一步调查。