Почему __getitem__ не может быть classmethod?
Предположим, что следующий класс:
class Class(object):
@classmethod
def getitem(*args):
print 'getitem %s' % (args,)
@classmethod
def __getitem__(*args):
print '__getitem__ %s' % (args,)
Метод getitem ведет себя как и ожидалось: он получает Class
как первый arg, но __getitem__
получает type
как первый аргумент arg:
calling Class.getitem(test)
getitem (<class '__main__.Class'>, 'test')
calling obj.getitem(test)
getitem (<class '__main__.Class'>, 'test')
calling Class[test]
'type' object has no attribute '__getitem__'
calling obj[test]
__getitem__ (<class '__main__.Class'>, 'test')
Какая там магия позади __getitem__
?
Ответы
Ответ 1
Специальные методы рассматриваются на классе, а не на экземпляре - в отличие от обычных методов, которые сначала проверяются на экземпляр. См. Специальный поиск методов в документах модели данных Python.
Думая о Class
как пример type
, это означает, что когда вы делаете
Class.getitem(test)
Вначале выглядит именно то, что вы говорите: метод в Class
собственные атрибуты, называемые getitem
. Но, когда вы используете
Class[test]
он пропускает это значение и переходит прямо к type
(являясь классом Class
или его метаклассом), и поэтому вызывает type.__getitem__(Class, test)
. Итак, что происходит не в том, что __getitem__
получает type
в качестве своего первого аргумента (он все равно получит Class
, как это делает, если вы явно Class.__getitem__(test)
), то его __getitem__
, который ищет Python в этого случая не существует. Чтобы он существовал, вам нужно определить свой собственный метакласс для Class
, который определяет его как метод экземпляра, а не определяет его на Class
как метод класса.
Ответ 2
Когда вы вызываете x[test]
, интерпретатор проверяет type(x)
для атрибута __getitem__
. В случае Class[test]
это метаклас Class
, т.е. type
. Если вы хотите иметь класс __getitem__
класса, определите его внутри нового метакласса. (Излишне говорить, что это своего рода волшебство, как и все, что вы делаете с метаклассами)
class Meta(type):
def __getitem__(self, arg):
print "__getitem__:", arg
class X(object):
__metaclass__ = Meta
X['hello'] # output: __getitem__ hello