什么时候为列表和 numpy 数组调用 __index__?
Posted
技术标签:
【中文标题】什么时候为列表和 numpy 数组调用 __index__?【英文标题】:When is `__index__` called for lists and numpy arrays? 【发布时间】:2018-09-15 15:32:54 【问题描述】:从几天前开始,直到阅读this question,我才知道__index__()
方法。在那之后,我一直在documentation、PEP 和other SO questions 中阅读它。
我了解到,每当在可以切片的对象中使用 []
运算符时(在我的情况下,我对列表、numpy 数组和 pandas 感兴趣),都会获得切片或索引的值,以便 @ 987654327@ 已完成。
但是,与其中一个问题一样,结果取决于使用的是 PyPy 还是 CPython,因此我决定检查何时使用 __index__
实际进行切片,何时未进行切片。我已经完成了以下操作(在 CPython 2.7.14 中):
lst = range(10)
array = np.arange(10)
series = pd.Series(lst)
并定义了以下类:
class MyIndex:
def __index__(self):
return 2
class MyInt(int):
def __index__(self):
return 3
class MyStr(str):
def __index__(self):
return 4
然后我尝试用这个使用的定义对象访问定义的对象,获得以下内容:
注意:出于可读性目的,我没有发布完整的错误消息。
对于 MyIndex
类,预期输出 2:
print lst[MyIndex()]
print array[MyIndex()]
print series[MyIndex()]
# Output:
2
2
AttributeError: MyIndex instance has no attribute '__trunc__'
对于 MyInt
类,预期输出 3:
# Case 1
print lst[MyInt()]
print array[MyInt()]
print series[MyInt()]
# Output
0
0
0
# Case 2
print lst[MyInt(2)]
print array[MyInt(2)]
print series[MyInt(2)]
# Output
2
2
2
对于 MyStr
类,预期输出 4:
# Case 1
print lst[MyStr()]
print array[MyStr()]
print series[MyStr()]
# Output
4
4
KeyError: ''
# Case 2
print lst[MyStr('a')]
print array[MyStr('a')]
print series[MyStr('a')]
# Output
4
IndexError: only integers, slices (`:`), ellipsis (`...`), numpy.newaxis (`None`) and integer or boolean arrays are valid indices
KeyError: 'a'
对此我真的很疑惑,主要是以下几点:
对于列表,使用__index__
方法,但不适用于int
及其子项。
Numpy 使用__index__
类似列表,但在最后一种情况下MyStr('a')
会引发错误。我是否遗漏了什么,或者在这种情况下 __index__
仅在 MyStr
为空字符串时使用?
Pandas 切片是一个完整的世界,甚至接受对有序字符串索引进行切片,因此不使用__index__
是一种解脱。因此,我对 pandas 的唯一问题是代码的输出是否会因 python 实现而有所不同。
我的问题基本上就是标题中的那个:
__index__
何时调用列表和 numpy 数组?为什么会有一些例外?
话虽如此,我很高兴收到我可能错过的有关此方法的任何额外信息。
【问题讨论】:
用__getitem__
方法定义一个类,看看索引给它的args
元组是很有启发性的。 numpy
index_tricks.py
使用它来创建一些伪索引函数,例如np.r_
和np.mgrid
。
只有在最近的版本中,numpy
才拒绝将浮点数作为索引。它曾经允许它们,根据需要截断。
【参考方案1】:
首先,将docs 引用为__index__
:
调用以实现 operator.index(),以及 Python 需要时 无损地将数字对象转换为整数对象(例如在 切片,或在内置 bin()、hex() 和 oct() 函数中)。 此方法的存在表明数字对象是 整数类型。必须返回一个整数。
注意:为了有一个连贯的整数类型类,当
__index__()
定义了__int__()
也应该定义,两者都应该返回 相同的值。
__index__
通常在对象已经是 int 时不会被调用,因为不需要转换。另外,你需要一个__int__
方法来搭配__index__
;你的一些问题来自于此。 (您的MyInt
继承了int.__int__
,但它的__index__
行为与它从int
继承的行为不一致,所以这也是一个问题。)
在 CPython 中,列表实现了 C 级别的序列协议,并且 CPython 在调用序列协议之前会自动为非整数调用 __index__
。 Int 只是使用它们的 int 值,而您的 MyInt()
的 int 值为 0。如果需要,您可以通过 PyObject_GetItem
、PyNumber_AsSsize_t
和 PyNumber_Index
跟踪 __index__
的调用链。
NumPy 数组不使用序列协议进行索引。他们实现了它,但他们也实现了优先级的映射协议。 NumPy 数组自己处理索引。
他们尝试的其中一个方法是PyNumber_Index
,这就是为什么它们在大多数测试中表现得像列表。但是,NumPy 数组支持比列表更复杂的索引,并且 NumPy 数组索引实现的一部分是weird special case,其中某些非元组序列被视为索引元组。
您的MyStr
对象是序列,MyStr('a')
会触发特殊情况。它被视为tuple(MyStr('a'))
或('a',)
,这不是有效的索引元组。
对于 Pandas,pandas.Series
在 Python 级别实现 __getitem__
。它还必须手动处理索引。
对于MyIndex()
,它似乎试图在您的MyIndex()
对象上调用int
,但由于您没有__int__
方法而失败。错误通常是 TypeError,Pandas 可能会以不同的方式处理它,但你忘记从 object
继承,所以你得到了一个经典的类,这些很奇怪。
您的 MyInt()
对象是整数,被用作整数,与列表和数组测试相同。
您的 MyStr()
对象是字符串,Pandas 将它们视为字符串,而不是尝试将它们解释为整数。
【讨论】:
现在我唯一的疑问是对于 index 返回的值与 int 不同的 int 应该如何处理?在 PyPy 中,它看起来像 index 优先(***.com/questions/49633222/…) @xg.plt.py:如果您的__int__
和您的__index__
不匹配,则您的对象已损坏。 Python 没有指定哪一个获胜,您从检查实现中收集到的任何内容都只是实现细节,如有更改,恕不另行通知。以上是关于什么时候为列表和 numpy 数组调用 __index__?的主要内容,如果未能解决你的问题,请参考以下文章