Понимание метода __getitem__

Я просмотрел большую часть документации __getitem__ в документах Python, а также в stackoverflow, поэтому это не дублирующий вопрос. Но я все еще не могу понять смысл этого.

Итак, я могу понять, что __getitem__ используется для реализации вызовов типа self[key]. Но в чем его польза?

Предположим, что у меня есть класс python, определенный таким образом:

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__ внутренне. Но почему он это делает?

Может кто-нибудь объяснить это более подробно?

Ответ 1

Конг Ма хорошо объясняет, для чего используется __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__ как это, на самом деле зависит от того, как вы планируете абстрагировать свои данные - в этом случае мы решили рассматривать здание как контейнер этажей (и вы могли бы также реализовать итератор для здания, и, возможно, даже способность срез - т.е. получать более одного этажа за один раз - это зависит от того, что вам нужно.

Ответ 2

Синтаксис [] для получения элемента по ключу или индексу является просто синтаксическим сахаром.

Когда вы оцениваете 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

Ответ 3

Магический метод __getitem__ в основном используется для доступа к элементам списка, элементам словаря, элементам массива и т.д. Он очень полезен для быстрого поиска атрибутов экземпляра.

Здесь я показываю это на примере класса Person, для которого могут быть созданы экземпляры 'name', 'age' и 'dob' (дата рождения). Метод __getitem__ написан таким образом, что можно получить доступ к индексированным атрибутам экземпляра, таким как имя или фамилия, день, месяц или год объекта dob и т.д.

import copy

# Constants that can be used to index date of birth 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'