Klasa Pythona dostępna przez iterator i indeks

Może to być pytanie n00b, ale obecnie mam klasę, która implementuje iterator, więc mogę zrobić coś takiego

for i in class():

Ale chcę mieć dostęp do klasy przez indeks jak również

class()[1]
Jak mogę to zrobić? Dzięki!
Author: xster, 2011-03-19

2 answers

Zaimplementuj oba __iter__() oraz __getitem__() metody et alia.

 41
Author: Ignacio Vazquez-Abrams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-19 02:43:41

Obecna zaakceptowana odpowiedź od @Ignacio Vazquez-Abrams jest wystarczająca. Jednak inni zainteresowani tym pytaniem mogą rozważyć dziedziczenie ich klasy z abstrakcyjnej klasy bazowej (takiej jak te Znalezione w standardowym module collections.abc). To robi wiele rzeczy ( są prawdopodobnie inne, jak również):

  • zapewnia, że wszystkie metody potrzebne do traktowania obiektu "jak _ _ _" są tam {47]}
  • jest samo-dokumentowanie, w tym, że ktoś czytający Twój kod jest w stanie natychmiast wiedzieć, że zamierzasz swój obiekt " zachowywać się jak ____".
  • Pozwala isinstance(myobject,SomeABC) działać poprawnie.
  • często dostarcza metody automatycznie magicznie, więc nie musimy sami ich definiować [47]}

(zauważ, że oprócz powyższego, tworzenie własnego ABC może pozwolić na sprawdzenie obecności określonej metody lub zestawu metod w dowolnym obiekcie i na tej podstawie zadeklarować, że obiekt jest podklasa ABC, nawet jeśli obiekt nie dziedziczy bezpośrednio z ABC. zobacz tę odpowiedź, aby uzyskać więcej informacji.)

Teraz jako przykład, wybierzmy i zaimplementujmy ABC dla klasy w oryginalnym pytaniu. Istnieją dwa wymagania:

  1. klasa jest iteracyjna
  2. dostęp do klasy przez indeks

Oczywiście, ta klasa będzie rodzajem kolekcji. Więc co zrobimy, to spojrzymy na nasze menu z collection ABC ' s, aby znaleźć odpowiednie ABC (zauważ, że istnieją również numeryczne ABCs ). Odpowiednie ABC zależy od tego, jakie abstrakcyjne metody chcemy zastosować w naszej klasie.

Widzimy, że Iterable jest tym, czego szukamy, jeśli chcemy użyć metody __iter__(), która jest nam potrzebna, aby robić takie rzeczy jak for o in myobject:. Jednak Iterable nie zawiera metody __getitem__(), która jest nam potrzebna, aby robić takie rzeczy jak myobject[i]. Więc musimy użyć innego ABC.

W menu collections.abc abstrakcyjnych klas bazowych widzimy, że Sequence jest najprostszym ABC oferującym wymaganą przez nas funkcjonalność. I - spójrzcie na to-otrzymujemy Iterable funkcjonalność jako metodę mixin - co oznacza, że nie musimy jej sami definiować-za darmo! Otrzymujemy również __contains__, __reversed__, index, i count. Które, jeśli się nad tym zastanowić, to wszystkie rzeczy, które powinny być zawarte w każdym zindeksowanym obiekcie. Jeśli zapomniałeś je uwzględnić, użytkownicy twojego kod (w tym, potencjalnie, siebie!) może być dość zirytowany(wiem, że tak).

Istnieje jednak drugie ABC, które oferuje również to połączenie funkcjonalności (iterable i accessible by []): a Mapping. Którego chcemy użyć?

Przypominamy, że wymaganiem jest możliwość dostępu do obiektu przez indeks (Jak list lub a tuple), tzn. nie według klucza (Jak dict). Dlatego wybieramy Sequence zamiast Mapping.

Ważne jest, aby pamiętać, że Sequence jest tylko do odczytu (tak jak Mapping), więc nie pozwoli nam to robić rzeczy takich jak myobject[i] = value, czy random.shuffle(myobject). Jeśli chcemy być w stanie robić takie rzeczy, musimy kontynuować w dół menu ABCs i użyć MutableSequence (lub MutableMapping).

Teraz jesteśmy w stanie zrobić naszą klasę. Definiujemy ją i dziedziczymy od Sequence.

from collections.abc import Sequence
class MyClass(Sequence):
    pass

Jeśli spróbujemy go użyć, interpreter powie nam, jakie metody musimy wdrożyć zanim będzie można go użyć (zauważ, że metody są również wymienione na stronie Python docs):

>>> 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__

To mówi nam, że jeśli będziemy wdrażać __getitem__ i __len__, będziemy mogli użyć naszej nowej klasy. Możemy to zrobić tak w Pythonie 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's 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!
To działa!
 56
Author: Rick Teachey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-14 15:32:38