Розуміння методу __getitem__


140

Я переглянув більшу частину документації __getitem__в документах Python, але все ще не можу зрозуміти сенс цього.

Тому я можу зрозуміти лише те, що __getitem__використовується для здійснення подібних дзвінків self[key]. Але в чому це користь?

Скажімо, у мене клас визначений таким чином:

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

Це повертає результати, як очікувалося. Але навіщо використовувати __getitem__в першу чергу? Я також чув, що Python дзвонить __getitem__всередину. Але чому це робиться?

Може хтось, будь ласка, пояснить це детальніше?


Це може зацікавити одне з прикладів використання: Як правильно підкласировать dict та переосмислювати getitem & setitem
roganjosh

4
__getitem__Використання в вашому прикладі не робить багато сенсу, але уявіть собі , що вам потрібно написати спеціальний list- або словник-подібного класу, який повинен працювати з існуючим кодом , який використовує []. Ось така ситуація __getitem__корисна.
Пітер Вітвок

Відповіді:


158

Cong Ma добре __getitem__допомагає пояснити, для чого використовується - але я хочу навести вам приклад, який може бути корисним. Уявіть собі клас, який моделює будівлю. Дані для будівлі містять ряд ознак, включаючи описи компаній, які займають кожен поверх:

Без використання у __getitem__нас був би такий клас:

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

Однак ми могли б використати __getitem__(та його аналог __setitem__) для того, щоб зробити клас 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] )

Чи будете ви користуватися __setitem__таким чином, насправді залежить від того, як ви плануєте абстрагувати свої дані - у цьому випадку ми вирішили ставитися до будівлі як до контейнера підлог (і ви також можете реалізувати ітератор для будівлі, а може навіть і можливість розрізання - тобто отримуйте дані більше ніж один поверх - це залежить від того, що вам потрібно.


15
Просто поділитися чимось, я дізнався, лише прочитавши відповідь кілька разів: коли у вас є гетьтема, вам не потрібно явно викликати цю функцію. Коли він називає building1[2]цей дзвінок, то внутрішньо називає гетьтема. Отже, пункт @ tony-sufolk-66 полягає в тому, що будь-яке властивість / змінну класу можна отримати під час виконання, просто викликавши ім'я об'єкта [ім'я змінної]. Просто уточнюючи це, оскільки для мене це було не зрозуміло спочатку, і писав це тут, сподіваючись, що це комусь допоможе. Видаліть, якщо
зайве,

3
@mithunpaul позначення об’єкта [index] не використовується для отримання властивості / змінної / атрибута класу - це індексація об'єкта контейнера - наприклад, отримання дочірнього об'єкта від батьків, де батько підтримує список своїх дітей. У моєму прикладі - клас Building є контейнером (в даному випадку назвами Floor), але це може бути клас контейнерів для класів Floor.
Тоні Суффолк 66,

За винятком того, що він не підтримує len(), і ви отримаєте TypeError:TypeError: object of type 'Building' has no len()
Ciasto piekarz

Підтримка len (та інші функції, такі як ітерація тощо) не була метою мого прикладу. Реалізація методу dunder_len є тривіальною.
Тоні Суффолк 66,

@ TonySuffolk66: це правильно, що ____len____ визначає ітерабельний для індексу (поверхів) у вашому прикладі, на якому ____getitem____ циклів?
Олексій

73

[]Синтаксис для отримання елемента по ключу або індексу тільки синтаксис.

Коли ви оцінюєте a[i]виклики Python a.__getitem__(i)(або type(a).__getitem__(a, i), але це відмінність стосується моделей успадкування і тут не важливо). Навіть якщо клас aможе не чітко визначати цей метод, він, як правило, успадковується від класу предків.

Усі назви спеціальних методів (Python 2.7) та їх семантика перераховані тут: https://docs.python.org/2.7/reference/datamodel.html#special-method-names


8

Магічний метод __getitem__в основному використовується для доступу до елементів списку, записів словника, елементів масиву тощо. Це дуже корисно для швидкого пошуку атрибутів екземплярів.

Ось я показую це на прикладі класу Person, який може бути описаний на ім'я, ім'я, вік та «доб» (дата народження). __getitem__Метод написаний таким чином , що можна отримати доступ до індексованих атрибутів примірника, наприклад, ім'я або прізвище, день, місяць або рік DOB і т.д.

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

Припустимо, один вхід користувача такий:

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

За допомогою __getitem__методу користувач може отримати доступ до індексованих атрибутів. наприклад,

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

Чудовий приклад! Я всюди шукав, як реалізувати getitem, коли в init є кілька параметрів, і я намагався знайти належну реалізацію і, нарешті, це побачив! Оголошено і дякую!
Рахул П
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.