Grundlegendes zur __getitem__ -Methode

137

Ich habe den größten Teil der Dokumentation von durchgesehen __getitem__ in den Python-Dokumenten , kann aber immer noch nicht verstehen, was das bedeutet.

Alles was ich verstehen kann ist, dass __getitem__es verwendet wird, um Aufrufe wie zu implementierenself[key] . Aber wozu dient es?

Nehmen wir an, ich habe eine Python-Klasse, die folgendermaßen definiert ist:

class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def __getitem__(self,key):
        print ("Inside `__getitem__` method!")
        return getattr(self,key)

p = Person("Subhayan",32)
print (p["age"])

Dies gibt die Ergebnisse wie erwartet zurück. Aber warum überhaupt verwenden __getitem__? Ich habe auch gehört, dass Python __getitem__intern anruft . Aber warum macht es das?

Kann jemand dies bitte genauer erklären?

user1867151
quelle
Dies kann für ein Beispiel von Interesse sein: Wie man diktiert und überschreibt getitem & setitem
roganjosh
4
Die __getitem__Verwendung in Ihrem Beispiel macht nicht viel Sinn, aber stellen Sie sich vor, Sie müssen eine benutzerdefinierte listen- oder wörterbuchähnliche Klasse schreiben, die mit vorhandenem Code arbeiten muss, der verwendet wird []. Das ist eine Situation, in der __getitem__es nützlich ist.
Pieter Witvoet

Antworten:

157

Cong Ma erklärt sehr gut, was __getitem__ verwendet wird - aber ich möchte Ihnen ein Beispiel geben, das nützlich sein könnte. Stellen Sie sich eine Klasse vor, die ein Gebäude modelliert. In den Daten für das Gebäude sind eine Reihe von Attributen enthalten, einschließlich Beschreibungen der Unternehmen, die jede Etage belegen:

Ohne Verwendung hätten __getitem__wir eine Klasse wie diese:

class Building(object):
     def __init__(self, floors):
         self._floors = [None]*floors
     def occupy(self, floor_number, data):
          self._floors[floor_number] = data
     def get_floor_data(self, floor_number):
          return self._floors[floor_number]

building1 = Building(4) # Construct a building with 4 floors
building1.occupy(0, 'Reception')
building1.occupy(1, 'ABC Corp')
building1.occupy(2, 'DEF Inc')
print( building1.get_floor_data(2) )

Wir könnten jedoch __getitem__(und sein Gegenstück __setitem__) verwenden, um die Verwendung der Gebäudeklasse "schöner" zu machen.

class Building(object):
     def __init__(self, floors):
         self._floors = [None]*floors
     def __setitem__(self, floor_number, data):
          self._floors[floor_number] = data
     def __getitem__(self, floor_number):
          return self._floors[floor_number]

building1 = Building(4) # Construct a building with 4 floors
building1[0] = 'Reception'
building1[1] = 'ABC Corp'
building1[2] = 'DEF Inc'
print( building1[2] )

Ob Sie __setitem__dies verwenden, hängt wirklich davon ab, wie Sie Ihre Daten abstrahieren möchten. In diesem Fall haben wir beschlossen, ein Gebäude als Container mit Etagen zu behandeln (und Sie könnten auch einen Iterator für das Gebäude implementieren und möglicherweise sogar die Fähigkeit zum Schneiden - dh mehr als eine Etage gleichzeitig abrufen - das hängt davon ab, was Sie benötigen.

Tony Suffolk 66
quelle
15
Nur um etwas zu teilen, was ich erst gelernt habe, nachdem ich die Antwort mehrmals gelesen habe : Sobald Sie ein Getitem haben, müssen Sie diese Funktion nicht explizit aufrufen. Wenn er building1[2]diesen Anruf selbst anruft , ruft er intern das getitem auf. Der Punkt, den @ tony-suffolk-66 macht, ist, dass jede Eigenschaft / Variable der Klasse zur Laufzeit durch einfaches Aufrufen des Objektnamens [Variablenname] abgerufen werden kann. Ich habe das nur geklärt, da es mir anfangs nicht klar war, und es hier geschrieben, in der Hoffnung, dass es jemandem hilft. Löschen Sie, wenn überflüssig bitte
mithunpaul
3
@mithunpaul Die Objekt [Index] -Notation wird nicht verwendet, um eine Eigenschaft / Variable / ein Attribut einer Klasse abzurufen. Sie indiziert für ein Containerobjekt. Beispielsweise wird ein untergeordnetes Objekt von einem übergeordneten Objekt abgerufen, in dem das übergeordnete Objekt eine Liste seiner untergeordneten Objekte verwaltet. In meinem Beispiel ist die Building-Klasse ein Container (in diesem Fall von Floor-Namen), es kann sich jedoch auch um eine Container-Klasse für Floor-Klassen handeln.
Tony Suffolk 66
Außer es wird nicht unterstützt len(), und Sie erhalten eine TypeError:TypeError: object of type 'Building' has no len()
Ciasto piekarz
Die Unterstützung von len (und anderen Funktionen wie Iteration usw.) war nicht der Zweck meines Beispiels. Die Implementierung einer dunder_len-Methode ist jedoch trivial.
Tony Suffolk 66
@ TonySuffolk66: Ist das richtig, dass ____len____ die Iterierbarkeit für den Index (Etagen) in Ihrem Beispiel bestimmt, auf dem ____getitem____ Schleifen?
Alex
72

Die []Syntax zum Abrufen eines Elements nach Schlüssel oder Index ist nur Syntaxzucker.

Wenn Sie bewerten a[i] Python-Aufrufea.__getitem__(i) (oder type(a).__getitem__(a, i), aber diese Unterscheidung betrifft Vererbungsmodelle und ist hier nicht wichtig). Auch wenn die Klasse von adiese Methode möglicherweise nicht explizit definiert, wird sie normalerweise von einer Vorgängerklasse geerbt.

Alle (Python 2.7) speziellen Methodennamen und ihre Semantik sind hier aufgelistet: https://docs.python.org/2.7/reference/datamodel.html#special-method-names

Cong Ma
quelle
8

Die magische Methode __getitem__wird im Wesentlichen für den Zugriff auf Listenelemente, Wörterbucheinträge, Array-Elemente usw. verwendet. Sie ist sehr nützlich für die schnelle Suche nach Instanzattributen.

Hier zeige ich dies anhand einer Beispielklasse Person, die durch 'Name', 'Alter' und 'Dob' (Geburtsdatum) instanziiert werden kann. Die __getitem__Methode ist so geschrieben, dass auf die indizierten Instanzattribute wie Vor- oder Nachname, Tag, Monat oder Jahr des Dob usw. zugegriffen werden kann.

import copy

# Constants that can be used to index date of birth's Date-Month-Year
D = 0; M = 1; Y = -1

class Person(object):
    def __init__(self, name, age, dob):
        self.name = name
        self.age = age
        self.dob = dob

    def __getitem__(self, indx):
        print ("Calling __getitem__")
        p = copy.copy(self)

        p.name = p.name.split(" ")[indx]
        p.dob = p.dob[indx] # or, p.dob = p.dob.__getitem__(indx)
        return p

Angenommen, eine Benutzereingabe lautet wie folgt:

p = Person(name = 'Jonab Gutu', age = 20, dob=(1, 1, 1999))

Mit Hilfe der __getitem__Methode kann der Benutzer auf die indizierten Attribute zugreifen. z.B,

print p[0].name # print first (or last) name
print p[Y].dob  # print (Date or Month or ) Year of the 'date of birth'
user3503692
quelle
Tolles Beispiel! Ich habe überall gesucht, wie man getitem implementiert, wenn es mehrere Parameter in init gibt, und ich hatte Mühe, eine richtige Implementierung zu finden, und habe dies endlich gesehen! Upvoted und danke!
Rahul P