Memahami metode __getitem__

140

Saya telah membaca sebagian besar dokumentasi __getitem__di dalam dokumen Python, tetapi saya masih tidak dapat memahami artinya.

Jadi yang bisa saya mengerti adalah yang __getitem__digunakan untuk mengimplementasikan panggilan seperti self[key]. Tapi apa gunanya?

Katakanlah saya memiliki kelas python yang ditentukan dengan cara ini:

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

Ini mengembalikan hasil seperti yang diharapkan. Tapi mengapa digunakan __getitem__di tempat pertama? Saya juga mendengar bahwa Python memanggil secara __getitem__internal. Tapi mengapa melakukannya?

Bisakah seseorang menjelaskan ini lebih detail?

pengguna1867151
sumber
Ini mungkin menarik untuk salah satu contoh penggunaan: Cara benar subclass dict dan menimpa getItem & setitem
roganjosh
4
The __getitem__digunakan dalam contoh Anda tidak membuat banyak akal, tapi bayangkan bahwa Anda perlu untuk menulis daftar-kustom atau kamus-seperti kelas, yang harus bekerja dengan kode yang sudah ada yang menggunakan []. Itu adalah situasi __getitem__yang berguna.
Pieter Witvoet

Jawaban:

158

Cong Ma melakukan pekerjaan yang baik dalam menjelaskan apa __getitem__yang digunakan - tapi saya ingin memberi Anda contoh yang mungkin berguna. Bayangkan sebuah kelas yang memodelkan sebuah bangunan. Di dalam data untuk bangunan itu mencakup sejumlah atribut, termasuk deskripsi perusahaan yang menempati setiap lantai:

Tanpa menggunakan __getitem__kita akan memiliki kelas seperti ini:

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

Namun kami dapat menggunakan __getitem__(dan mitranya __setitem__) untuk membuat penggunaan kelas Bangunan 'lebih bagus'.

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

Apakah Anda menggunakan __setitem__seperti ini benar-benar tergantung pada bagaimana Anda berencana untuk mengabstraksi data Anda - dalam hal ini kami telah memutuskan untuk memperlakukan sebuah bangunan sebagai wadah lantai (dan Anda juga dapat menerapkan iterator untuk Bangunan tersebut, dan bahkan mungkin kemampuan untuk mengiris - yaitu mendapatkan lebih dari satu data lantai sekaligus - tergantung pada apa yang Anda butuhkan.

Tony Suffolk 66
sumber
15
Hanya untuk membagikan sesuatu yang saya pelajari hanya setelah membaca jawabannya beberapa kali: setelah Anda memiliki getitem, Anda tidak perlu secara eksplisit memanggil fungsi itu. Ketika dia memanggil building1[2]panggilan itu sendiri secara internal memanggil getitem tersebut. Jadi poin yang dibuat @ tony-suffolk-66 adalah, setiap properti / variabel kelas dapat diambil selama run time hanya dengan memanggil nama objek [variablename]. Hanya mengklarifikasi ini karena awalnya tidak jelas bagi saya dan menulisnya di sini berharap itu membantu seseorang. Hapus jika berlebihan, silakan
mithunpaul
3
@mithunpaul notasi objek [indeks] tidak digunakan untuk mendapatkan properti / variabel / atribut dari kelas - ia mengindeks pada objek kontainer - misalnya mengambil objek turunan dari induk di mana induk memelihara daftar anaknya. Dalam contoh saya - kelas Bangunan adalah wadah (dalam hal ini nama Lantai), tetapi bisa juga kelas wadah untuk kelas Lantai.
Tony Suffolk 66
Kecuali itu tidak akan mendukung len(), dan Anda akan mendapatkan TypeError:TypeError: object of type 'Building' has no len()
Ciasto piekarz
Mendukung len (dan fitur lain seperti iterasi dll) bukanlah tujuan dari contoh saya. Menerapkan metode dunder_len itu sepele.
Tony Suffolk 66
@ TonySuffolk66: apakah ini benar bahwa ____len____ menentukan iterable untuk indeks (lantai) dalam contoh Anda di mana ____getitem____ mengulang?
Alex
73

The []sintaks untuk mendapatkan item dengan kunci atau indeks hanya gula sintaks.

Saat Anda mengevaluasi a[i]panggilan Python a.__getitem__(i)(atau type(a).__getitem__(a, i), tetapi perbedaan ini adalah tentang model pewarisan dan tidak penting di sini). Meskipun kelas dari amungkin tidak secara eksplisit mendefinisikan metode ini, itu biasanya diwarisi dari kelas leluhur.

Semua nama metode khusus (Python 2.7) dan semantiknya tercantum di sini: https://docs.python.org/2.7/reference/datamodel.html#special-method-names

Cong Ma
sumber
8

Metode ajaib __getitem__pada dasarnya digunakan untuk mengakses item daftar, entri kamus, elemen array, dll. Ini sangat berguna untuk pencarian cepat atribut contoh.

Di sini saya menunjukkan ini dengan contoh class Person yang dapat dipakai oleh 'name', 'age', dan 'dob' (tanggal lahir). The __getitem__Metode ditulis dengan cara yang satu dapat mengakses contoh atribut diindeks, seperti pertama atau terakhir nama, hari, bulan atau tahun dob ini, dll

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

Misalkan salah satu input pengguna adalah sebagai berikut:

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

Dengan bantuan __getitem__metode, pengguna dapat mengakses atribut yang diindeks. misalnya,

print p[0].name # print first (or last) name
print p[Y].dob  # print (Date or Month or ) Year of the 'date of birth'
pengguna3503692
sumber
Contoh yang bagus! Saya sedang mencari di mana-mana tentang bagaimana menerapkan getitem ketika ada beberapa parameter di init dan saya berjuang untuk menemukan implementasi yang tepat dan akhirnya melihat ini! Suara positif dan terima kasih!
Rahul P