Итерация объекта класса - PullRequest
2 голосов
/ 20 июня 2011

Это не настоящая программа, но я хотел бы знать, почему это не может быть сделано.

Я думал о numpy.r_ объекте и пытался сделать что-то подобное, но просто создал класс и не создает его .

простой код (имеет некоторые недостатки) для целых чисел может быть:

class r_:
    @classmethod
    def __getitem__(clc, sl):
        try:
            return range(sl)
        except TypeError:
            sl = sl.start, sl.stop, sl.step
            return range(*(i for i in sl if i is not None))

, но, как я пытаюсь сделать r_[1:10], я получаю TypeError: 'type' object is not subscriptable.

Конечно, код работает с r_.__getitem__(slice(1,10)), но это не то, что я хочу.

Есть ли что-то, что я могу сделать в этом случае вместо использования r_()[1:10]?

Ответы [ 4 ]

3 голосов
/ 20 июня 2011

Протокол для разрешения obj[index] заключается в поиске метода __getitem__ в типе из obj, , а не для прямого поиска метода в obj (который обычно прибегает к поиску метода для типа, если obj не имеет атрибута экземпляра с именем __getitem__).

Это легко проверить.

>>> class Foo(object):
    pass

>>> def __getitem__(self, index):
    return index

>>> f = Foo()
>>> f.__getitem__ = __getitem__
>>> f[3]
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    f[3]
TypeError: 'Foo' object does not support indexing
>>> Foo.__getitem__ = __getitem__
>>> f[3]
3

Я не знаю, почему именно это работает, но я бы предположил, что, по крайней мере, одна из причин именно в том, чтобы предотвратить то, что вы пытаетесь сделать; было бы удивительно, если бы каждый класс, который определил __getitem__ так, чтобы его экземпляры были индексируемыми, случайно получил возможность индексироваться сам. В подавляющем большинстве случаев код, пытающийся индексировать класс, будет ошибкой, поэтому, если метод __getitem__ сможет что-то вернуть, было бы плохо, если бы его не поймали.

Почему бы вам просто не назвать класс как-нибудь еще и не связать его экземпляр с именем r_? Тогда вы сможете сделать r_[1:10].

2 голосов
/ 20 июня 2011

То, что вы пытаетесь сделать, похоже на list[1:5] или set[1:5] =) Специальный метод __getitem__ работает только на экземплярах.

Что обычно нужно сделать, это просто создать один ("singleton") экземпляр класса:

class r_class(object):
    ...

r_ = r_class()

Теперь вы можете сделать:

r_[1:5]

Вы также можете использовать метаклассы, но это может быть больше, чем необходимо.

" Нет, мой вопрос был о getitem в классе, а не в экземпляре "

Тогда вам нужны метаклассы.

class r_meta(type):
    def __getitem__(cls, key):
        return range(key)
class r_(object, metaclass=r_meta):
    pass

Демо-версия:

>>> r_[5]
range(0, 5)

Если вы передадите r_[1:5], вы получите slice объект. Do help(slice) для получения дополнительной информации; вы можете получить доступ к таким значениям, как key.stop if isinstance(key,slice) else key.

1 голос
/ 20 июня 2011

Причина такого поведения заключается в способе поиска специальных методов, таких как __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 : Должен быть один - и желательно только один - очевидный способ сделать это.

1 голос
/ 20 июня 2011

Определите __getitem__() как обычный метод в r_ метаклассе .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...