Подтвердить что ты не робот

Класс Python, доступный итератором и индексом

Возможно, это вопрос n00b, но в настоящее время у меня есть класс, который реализует итератор, поэтому я могу сделать что-то вроде

for i in class():

но я хочу иметь доступ к классу по индексу, а также

class()[1]

Как я могу это сделать?

Спасибо!

4b9b3361

Ответ 2

Текущий принятый ответ от @Ignacio Vazquez-Abrams достаточен. Однако другие, заинтересованные в этом вопросе, могут захотеть наследовать свой класс от абстрактного базового класса (ABC) (например, найденного в стандартном модуле collections.abc). Это делает ряд вещей (возможно, есть и другие):

  • гарантирует, что все методы, необходимые для обработки вашего объекта "как ____", существуют
  • это самодокументирование, потому что кто-то, читающий ваш код, может мгновенно узнать, что вы намерены "вести себя как ____".
  • позволяет isinstance(myobject,SomeABC) работать правильно.
  • часто предоставляет методы автоматически, поэтому нам не нужно определять их самим

(Обратите внимание, что в дополнение к вышесказанному, создание собственного ABC может позволить вам проверить наличие определенного метода или набора методов в любом объекте и на основе этого объявить этот объект подклассом ABC, даже если объект не наследуется от ABC напрямую. См. этот ответ для получения дополнительной информации.)


Пример: реализовать доступный только для чтения list -like с использованием ABC

Теперь в качестве примера, давайте выберем и реализуем ABC для класса в исходном вопросе. Есть два требования:

  1. класс повторяется
  2. получить доступ к классу по индексу

Очевидно, этот класс будет своего рода коллекцией. Итак, что мы будем делать, это посмотреть в нашем меню ABC collection , чтобы найти соответствующие ABC (обратите внимание, что есть также numeric ABC). Соответствующий ABC зависит от того, какие абстрактные методы мы хотим использовать в нашем классе.

Мы видим, что Iterable - это то, к чему мы стремимся, если мы хотим использовать метод __iter__(), который нам нужен для того, чтобы сделать что-то вроде for o in myobject: Тем не менее, Iterable не включает метод __getitem__(), который нам нужен для таких вещей, как myobject[i]. Поэтому нам нужно использовать другую ABC.

На вниз collections.abc меню абстрактных базовых классов, мы видим, что Sequence является простейшим ABC, чтобы предложить нам функциональность требуется. И - посмотрите на это - мы получаем функциональность Iterable в виде смешанного метода, что означает, что нам не нужно определять его самостоятельно - бесплатно! Мы также получаем __contains__, __reversed__, index и count. Которые, если вы думаете об этом, все вещи, которые должны быть включены в любой индексированный объект. Если бы вы забыли включить их, пользователи вашего кода (включая, возможно, самих себя!) Могли бы быть довольно раздражены (я знаю, что я буду).

Тем не менее, есть второй ABC который также предлагает эту комбинацию функциональности (итеративную и доступную с помощью []): Mapping. Какой из них мы хотим использовать?

Напомним, что требование должно иметь возможность доступа к объекту по индексу (например, list или tuple), то есть не по ключу (например, как dict). Поэтому мы выбираем Sequence вместо Mapping.


Боковая панель. Важно отметить, что Sequence myobject[i] = value только для чтения (как и Mapping), поэтому она не позволяет нам делать такие вещи, как myobject[i] = value или random.shuffle(myobject). Если мы хотим иметь возможность делать подобные вещи, нам нужно продолжить вниз по меню ABC и использовать MutableSequence (или MutableMapping), что потребует реализации нескольких дополнительных методов.


Пример кода

Теперь мы можем сделать наш класс. Мы определяем это, и имеем его наследовать от Sequence.

from collections.abc import Sequence

class MyClass(Sequence):
    pass

Если мы попытаемся использовать его, интерпретатор скажет нам, какие методы нам нужно реализовать, прежде чем его можно будет использовать (обратите внимание, что эти методы также перечислены на странице документации Python):

>>> myobject = MyClass()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyClass with abstract methods __getitem__, __len__

Это говорит нам о том, что если мы продолжим реализацию __getitem__ и __len__, мы сможем использовать наш новый класс. Мы могли бы сделать это так в Python 3:

from collections.abc import Sequence

class MyClass(Sequence):
    def __init__(self,L):
        self.L = L
        super().__init__()
    def __getitem__(self, i):
        return self.L[i]
    def __len__(self):
        return len(self.L)

# Let test it:
myobject = MyClass([1,2,3])
try:
    for idx,_ in enumerate(myobject):
        print(myobject[idx])
except Exception:
    print("Gah! No good!")
    raise
# No Errors!

Оно работает!