Почему __getattr__ не вызывается для операций индексации?

Мой вопрос:

Кажется, что __getattr__ не вызывается для операций индексации, т.е. я не могу использовать __getattr__ в классе A для предоставления A[...]. Есть причина для этого? Или способ обойти это, чтобы __getattr__ мог предоставлять эту функциональность без необходимости явно определять __getitem__, __setitem__ и т. д. на A?

Минимальный пример:

Допустим, я определяю два почти идентичных класса, Explicit и Implicit. Каждый создает небольшой список self._arr при инициации, и каждый определяет __getattr__, который просто передает все запросы атрибутов self._arr. Единственное отличие состоит в том, что Explicit также определяет __getitem__ (просто передавая его 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)

Это работает так, как ожидалось:

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

Но это не так:

>>> 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

person Sam Bader    schedule 04.01.2017    source источник
comment
Потому что именно так был разработан Python. __getattr__ используется для доступа к атрибутам, а __getitem__ используется для доступа к индексации. У каждого есть определенная роль. Я не уверен, в чем вопрос   -  person 3Doubloons    schedule 04.01.2017
comment
Этот ответ объясняет это.   -  person shx2    schedule 04.01.2017
comment
Что касается того, почему они являются отдельными операциями, подумайте вот о чем: d = {'keys': 0} В этом случае d.keys и d['keys'] — это очень разные вещи.   -  person 3Doubloons    schedule 04.01.2017
comment
как python мог догадаться, что доступ к индексу Implicit приводит к доступу к индексу одного из его членов? это вообще не имеет смысла.   -  person Jean-François Fabre    schedule 04.01.2017
comment
@ shx2 Спасибо, это именно мое замешательство!   -  person Sam Bader    schedule 04.01.2017


Ответы (1)


Python обходит __getattr__, __getattribute__ и экземпляр dict при поиске «специальных» методов для реализации языковой механики. (По большей части специальные методы имеют два символа подчеркивания с каждой стороны имени.) Если вы ожидали, что i[0] вызовет i.__getitem__(0), который, в свою очередь, вызовет i.__getattr__('__getitem__')(0), вот почему этого не произошло.

person user2357112 supports Monica    schedule 04.01.2017
comment
Для тех, кто ищет, комментарий @shx2 выше ссылается на вопрос со ссылкой на это. - person Sam Bader; 04.01.2017