Ответ 1
Внесите обе __iter__()
и __getitem__()
и др. методы.
Возможно, это вопрос n00b, но в настоящее время у меня есть класс, который реализует итератор, поэтому я могу сделать что-то вроде
for i in class():
но я хочу иметь доступ к классу по индексу, а также
class()[1]
Как я могу это сделать?
Спасибо!
Внесите обе __iter__()
и __getitem__()
и др. методы.
Текущий принятый ответ от @Ignacio Vazquez-Abrams достаточен. Однако другие, заинтересованные в этом вопросе, могут захотеть наследовать свой класс от абстрактного базового класса (ABC
) (например, найденного в стандартном модуле collections.abc
). Это делает ряд вещей (возможно, есть и другие):
isinstance(myobject,SomeABC)
работать правильно. (Обратите внимание, что в дополнение к вышесказанному, создание собственного ABC
может позволить вам проверить наличие определенного метода или набора методов в любом объекте и на основе этого объявить этот объект подклассом ABC
, даже если объект не наследуется от ABC
напрямую. См. этот ответ для получения дополнительной информации.)
list
-like с использованием ABC
Теперь в качестве примера, давайте выберем и реализуем ABC
для класса в исходном вопросе. Есть два требования:
Очевидно, этот класс будет своего рода коллекцией. Итак, что мы будем делать, это посмотреть в нашем меню 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!