0

My question:

It seems that __getattr__ is not called for indexing operations, ie I can't use __getattr__ on a class A to provide A[...]. Is there a reason for this? Or a way to get around it so that __getattr__ can provide that functionality without having to explicitly define __getitem__, __setitem__, etc on A?

Minimal Example:

Let's say I define two nearly identical classes, Explicit and Implicit. Each creates a little list self._arr on initiation, and each defines a __getattr__ that just passes all attribute requests to self._arr. The only difference is that Explicit also defines __getitem__ (by just passing it on to self._arr).

# Passes all attribute requests on to a list it contains
class Explicit():
   def __init__(self):
        self._arr=[1,2,3,4]
    def __getattr__(self,attr):
        print('called __getattr_')
        return getattr(self._arr,attr)
    def __getitem__(self,item):
        return self._arr[item]

# Same as above but __getitem__ not defined
class Implicit():
    def __init__(self):
        self._arr=[1,2,3,4]
    def __getattr__(self,attr):
        print('called __getattr_')
        return getattr(self._arr,attr)

This works as expected:

>>> e=Explicit()
>>> print(e.copy())
called __getattr_
[1, 2, 3, 4]
>>> print(hasattr(e,'__getitem__'))
True
>>> print(e[0])
1

But this doesn't:

>>> i=Implicit()
>>> print(i.copy())
called __getattr_
[1, 2, 3, 4]
>>> print(hasattr(i,'__getitem__'))
called __getattr_
True
>>> print(i.__getitem__(0))
called __getattr_
1
>>> print(i[0])
TypeError: 'Implicit' object does not support indexing
Oleg Bogdanov
  • 1,712
  • 13
  • 19
Sam Bader
  • 185
  • 1
  • 7
  • 7
    Because that's how Python was designed. `__getattr__` is used for attribute access and `__getitem__` is used for indexing access. Each has a specific role. I'm not sure what the question is – 3Doubloons Jan 04 '17 at 19:29
  • 1
    [This answer](http://stackoverflow.com/a/10376655/2096752) explains it. – shx2 Jan 04 '17 at 19:29
  • 2
    As for why they're separate operations, consider this: `d = {'keys': 0}` In this case, `d.keys` and `d['keys']` are very different things – 3Doubloons Jan 04 '17 at 19:31
  • how could python guess that accessing an index of `Implicit` accesses an index of one of its members? it makes no sense at all. – Jean-François Fabre Jan 04 '17 at 19:31
  • 1
    @shx2 Thank you, that's exactly my confusion! – Sam Bader Jan 04 '17 at 19:40

1 Answers1

4

Python bypasses __getattr__, __getattribute__, and the instance dict when looking up "special" methods for implementing language mechanics. (For the most part, special methods are ones with two underscores on each side of the name.) If you were expecting i[0] to invoke i.__getitem__(0), which would in turn invoke i.__getattr__('__getitem__')(0), that's why that didn't happen.

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • 1
    For anyone looking, @shx2 's comment above links to [a question with a reference for this](http://stackoverflow.com/a/10376655/2096752). – Sam Bader Jan 04 '17 at 19:46