Причина такого поведения заключается в способе поиска специальных методов, таких как __getitem__()
.
Атрибуты ищутся первыми в объектах __dict__
и, если их там нет, в классе __dict__
. Вот почему, например, это работает:
>>> class Test1(object):
... x = 'hello'
...
>>> t = Test1()
>>> t.__dict__
{}
>>> t.x
'hello'
Методы, определенные в теле класса, хранятся в классе __dict__
:
>>> class Test2(object):
... def foo(self):
... print 'hello'
...
>>> t = Test2()
>>> t.foo()
hello
>>> Test2.foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method foo() must be called with Test2 instance as first argument (got nothing
instead)
Пока здесь нет ничего удивительного. Однако когда дело доходит до специальных методов, поведение Python немного (или очень) отличается:
>>> class Test3(object):
... def __getitem__(self, key):
... return 1
...
>>> t = Test3()
>>> t.__getitem__('a key')
1
>>> Test3['a key']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'type' object is unsubscriptable
Сообщения об ошибках очень разные. С Test2 Python жалуется на несвязанный вызов метода, тогда как с Test3 он жалуется на неподписываемость.
Если вы попытаетесь вызвать специальный метод - посредством использования связанного с ним оператора - на объекте , Python не попытается найти его в объектах __dict__
, а сразу перейдет к __dict__
класса объекта , который, если сам объект является классом, является метаклассом . Вот где вы должны это определить:
>>> class Test4(object):
... class __metaclass__(type):
... def __getitem__(cls, key):
... return 1
...
>>> Test4['a key']
1
Другого пути нет. Процитируем PEP20 : Должен быть один - и желательно только один - очевидный способ сделать это.