title: When is `__index__` called for lists and numpy arrays?
body: <p>Since some days ago I was unaware of the <code>__index__()</code> method until reading <a href="https://stackoverflow.com/questions/49633222/slicing-elements-from-a-python-list-using-boolean-indexing">this question</a>. After that, I have been reading about it in the <a href="https://docs.python.org/3/reference/datamodel.html#object.__index__" rel="nofollow noreferrer">documentation</a>, <a href="https://docs.python.org/2.5/whatsnew/pep-357.html" rel="nofollow noreferrer">PEP</a> and in <a href="https://stackoverflow.com/questions/27449310/python-index-special-method">other SO questions</a>.</p>
  
  <p>I understood that whenever the <code>[]</code> operator was used in objects that could be sliced (in my case I'm interested in lists, numpy arrays and pandas), the value either for slicing or for indexing was obtained so that <code>lst[key]=lst[key.__index__()]</code> was fulfilled. </p>
  
  <p>However, as in one of the questions, the result depended on whether PyPy or CPython was used, so I decided to check when was slicing actually done with <code>__index__</code> and when it was not. I have done the following (in CPython 2.7.14):</p>
  
  <pre><code>lst = range(10)
  array = np.arange(10)
  series = pd.Series(lst)
  </code></pre>
  
  <p>And defined the following classes:</p>
  
  <pre><code>class MyIndex:
      def __index__(self):
          return 2
  class MyInt(int):
      def __index__(self):
          return 3
  class MyStr(str):
      def __index__(self):
          return 4
  </code></pre>
  
  <p>Then I tried to acces the defined objects with this used defined objects, obtaining the following:</p>
  
  <p>Note: I am not posting the complete error message for readability purposes. </p>
  
  <p>For <code>MyIndex</code> class, expected output 2:</p>
  
  <pre><code>print lst[MyIndex()]
  print array[MyIndex()]
  print series[MyIndex()]
  # Output:
  2
  2
  AttributeError: MyIndex instance has no attribute '__trunc__'
  </code></pre>
  
  <p>For <code>MyInt</code> class, expected output 3:</p>
  
  <pre><code># 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
  </code></pre>
  
  <p>For <code>MyStr</code> class, expected output 4:</p>
  
  <pre><code># 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'
  </code></pre>
  
  <p>I'm really puzzled by this, mainly by the following points:</p>
  
  <ul>
  <li>With lists the <code>__index__</code> method is used but not for <code>int</code> and its childrens. </li>
  <li>Numpy uses <code>__index__</code> like lists, but in the last case <code>MyStr('a')</code> raises an error. Am I missing something or in this case <code>__index__</code> is only used when <code>MyStr</code> is an empty string?</li>
  <li>Pandas slicing is a whole world and even accepts slicing for ordered string index, so it is a relieve that <code>__index__</code> is not used. Thus, my only question about pandas is if the output of a code could be different depending on the python implementation.</li>
  </ul>
  
  <p>My question is basically the one in the title:</p>
  
  <blockquote>
    <p><strong>When is <code>__index__</code> called for lists and numpy arrays? Why are there some exceptions?</strong></p>
  </blockquote>
  
  <p>Having said that, I will be happy to recieve any extra information I may have missed about this method.</p>