几天前我在阅读this question之前不知道__index__()
方法。之后,我一直在documentation,PEP和other SO questions中阅读相关内容。
我明白只要在可以切片的对象中使用[]
运算符(在我的情况下我对列表,numpy数组和pandas感兴趣),就可以获得切片或索引的值已完成lst[key]=lst[key.__index__()]
。
然而,正如在其中一个问题中,结果取决于是否使用了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
及其子级。 __index__
之类的列表,但在最后一种情况下,MyStr('a')
会引发错误。我错过了什么或者在这种情况下__index__
仅在MyStr
为空字符串时使用?__index__
不被使用。因此,我唯一关于pandas的问题是,如果代码的输出可能因python实现而有所不同。我的问题基本上就是标题中的问题:
__index__
何时调用列表和numpy数组?为什么会有例外?
话虽如此,我很乐意收到有关这种方法的任何额外信息。
答案 0 :(得分:1)
首先,引用__index__
的{{3}}:
如果对象已经是int,则通常不会调用调用实现operator.index(),并且只要Python需要 无损地将数字对象转换为整数对象(例如 切片,或者在内置的bin(),hex()和oct()函数中)。 此方法的存在表明数字对象是 整数类型。必须返回一个整数。
注意:为了拥有一个连贯的整数类型,
__index__()
定义__int__()
也应定义,两者都应该返回 相同的价值。
__index__
,因为不需要转换。此外,您还需要使用__int__
方法__index__
;你的一些问题来自于此。 (您的MyInt
会继承int.__int__
,但其__index__
行为与从int
继承的行为不一致,因此这也是一个问题。)
在CPython中,list实现了C级序列协议,CPython在调用序列协议之前自动调用__index__
非int。 Ints只是使用了他们的int值,而你的MyInt()
的int值为0.你可以通过docs,PyObject_GetItem
和{{跟踪__index__
的调用链3}}如果你想要。
NumPy数组不使用序列协议进行索引。他们实现了它,但他们也实现了优先级映射协议。 NumPy数组自己处理索引处理。
他们尝试的一件事是PyNumber_Index
,这就是为什么他们的行为就像大多数测试的列表一样。但是,NumPy数组支持比列表更复杂的索引,NumPy数组索引实现的一部分是PyNumber_AsSsize_t
,其中某些非元组序列被视为索引元组。
您的MyStr
个对象是序列,MyStr('a')
触发特殊情况。它被视为tuple(MyStr('a'))
或('a',)
,这不是有效的索引元组。
对于Pandas,pandas.Series
在Python级别实现__getitem__
。它还必须手动处理索引。
对于MyIndex()
,它似乎试图在您的int
对象上调用MyIndex()
,但由于您没有__int__
方法,该对象失败了。错误通常是一个TypeError,Pandas可能会以不同的方式处理,但你忘了继承object
,所以你得到了一个经典类,这些都很奇怪。
您的MyInt()
个对象是整数并用作整数,与列表和数组测试相同。
您的MyStr()
个对象是字符串,Pandas将它们视为字符串,而不是试图将它们解释为整数。