Comprensione del metodo __getitem__


140

Ho esaminato la maggior parte della documentazione nei documenti di __getitem__Python, ma non sono ancora in grado di comprenderne il significato.

Quindi tutto quello che posso capire è che __getitem__viene utilizzato per implementare chiamate come self[key]. Ma a cosa serve?

Diciamo che ho una classe python definita in questo modo:

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"])

Ciò restituisce i risultati come previsto. Ma perché usare __getitem__in primo luogo? Ho anche sentito che Python chiama __getitem__internamente. Ma perché lo fa?

Qualcuno può spiegarlo in modo più dettagliato?


Questo può essere di interesse per un esempio di utilizzo: come sottoclassare correttamente dict e sovrascrivere getitem e setitem
roganjosh

4
L' __getitem__uso nel tuo esempio non ha molto senso, ma immagina di dover scrivere un elenco personalizzato o una classe simile a un dizionario, che deve funzionare con il codice esistente che utilizza []. Questa è una situazione in cui __getitem__è utile.
Pieter Witvoet

Risposte:


158

Cong Ma fa un buon lavoro __getitem__nello spiegare a cosa serve, ma voglio darti un esempio che potrebbe essere utile. Immagina una classe che modella un edificio. All'interno dei dati per l'edificio include una serie di attributi, comprese le descrizioni delle aziende che occupano ogni piano:

Senza usare __getitem__avremmo una classe come questa:

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

Potremmo comunque usare __getitem__(e la sua controparte __setitem__) per rendere più "gradevole" l'utilizzo della classe Building.

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] )

Che tu lo usi in __setitem__questo modo dipende davvero da come intendi astrarre i tuoi dati: in questo caso abbiamo deciso di trattare un edificio come un contenitore di piani (e potresti anche implementare un iteratore per l'Edificio, e forse anche la capacità di suddividere - cioè ottieni più dati di un piano alla volta - dipende da cosa hai bisogno.


15
Giusto per condividere qualcosa che ho imparato solo dopo aver letto la risposta più volte: una volta che hai un getitem non devi chiamare esplicitamente quella funzione. Quando chiama building1[2]quella chiamata, internamente chiama getitem. Quindi il punto che @ tony-suffolk-66 sta facendo è che, qualsiasi proprietà / variabile della classe può essere recuperata durante il runtime semplicemente chiamando objectname [variablename]. Chiarisco solo questo dato che inizialmente non era chiaro per me e scriverlo qui sperando che aiuti qualcuno. Elimina se ridondante per favore
mithunpaul

3
@mithunpaul la notazione dell'oggetto [indice] non è usata per ottenere una proprietà / variabile / attributo di una classe - è indicizzante su un oggetto contenitore - per esempio il recupero di un oggetto figlio da un genitore in cui il genitore mantiene un elenco dei suoi figli. Nel mio esempio, la classe Building è un contenitore (in questo caso di nomi Floor), ma potrebbe essere una classe contenitore per le classi Floor.
Tony Suffolk 66

Solo che non supporterà len(), e riceverai un TypeError:TypeError: object of type 'Building' has no len()
Ciasto piekarz

Supportare len (e altre funzionalità come l'iterazione ecc.) Non era lo scopo del mio esempio. L'implementazione di un metodo dunder_len è tuttavia banale.
Tony Suffolk 66

@ TonySuffolk66: è corretto che ____len____ determina l'iterabile per index (floor) nel tuo esempio su quali loop ____getitem____?
Alex

73

La []sintassi per ottenere un elemento per chiave o indice è solo lo zucchero di sintassi.

Quando si valutano le a[i]chiamate Python a.__getitem__(i)(o type(a).__getitem__(a, i), ma questa distinzione riguarda i modelli di ereditarietà e non è importante qui). Anche se la classe di apotrebbe non definire esplicitamente questo metodo, di solito viene ereditato da una classe antenata.

Tutti i nomi dei metodi speciali (Python 2.7) e la loro semantica sono elencati qui: https://docs.python.org/2.7/reference/datamodel.html#special-method-names


8

Il metodo magico __getitem__è fondamentalmente utilizzato per accedere a voci di elenco, voci di dizionario, elementi di array, ecc. È molto utile per una rapida ricerca degli attributi di istanza.

Qui lo mostro con un esempio di classe Person che può essere istanziato da 'name', 'age' e 'dob' (data di nascita). Il __getitem__metodo è scritto in modo da poter accedere agli attributi dell'istanza indicizzata, come nome o cognome, giorno, mese o anno del dob, ecc.

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

Supponiamo che un input dell'utente sia il seguente:

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

Con l'aiuto del __getitem__metodo, l'utente può accedere agli attributi indicizzati. per esempio,

print p[0].name # print first (or last) name
print p[Y].dob  # print (Date or Month or ) Year of the 'date of birth'

Ottimo esempio! Stavo cercando dappertutto come implementare getitem quando ci sono più parametri in init e stavo lottando per trovare una corretta implementazione e finalmente l'ho visto! Votato e grazie!
Rahul P
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.