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

140

Я просмотрел большую часть документации __getitem__Python, но все еще не могу понять ее смысл.

Итак, все, что я могу понять, это то, что __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__внутренние вызовы . Но почему он это делает?

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

user1867151
источник
Это может быть интересно для одного примера использования: Как правильно создать подклассы dict и переопределить getitem и setitem
roganjosh
4
__getitem__Использование в вашем примере не делает много смысла, но представьте себе , что вам нужно написать специальный list- или словарь-подобного класса, который должен работать с существующим кодом , который использует []. Это ситуация, когда __getitem__это полезно.
Питер Витвоет

Ответы:

158

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

Тони Саффолк 66
источник
15
Просто чтобы поделиться тем, что я узнал только после того, как прочитал ответ несколько раз: если у вас есть getitem, вам не нужно явно вызывать эту функцию. Когда он вызывает building1[2]этот вызов, он внутренне вызывает getitem. Итак, суть @ tony-suffolk-66 заключается в том, что любое свойство / переменную класса можно получить во время выполнения, просто вызвав objectname [variablename]. Просто проясняю это, поскольку изначально мне было непонятно, и пишу здесь, надеясь, что это кому-то поможет. Удалите, если
повторяется,
3
@mithunpaul нотация объекта [индекс] не используется для получения свойства / переменной / атрибута класса - это индексирование по объекту-контейнеру - например, получение дочернего объекта от родителя, где родитель поддерживает список своих потомков. В моем примере класс Building - это контейнер (в данном случае - имена этажей), но это может быть класс контейнера для классов этажей.
Тони Саффолк 66,
Только он не будет поддерживать len(), и вы получите TypeError:TypeError: object of type 'Building' has no len()
Ciasto piekarz
Поддержка len (и других функций, таких как итерация и т. Д.) Не была целью моего примера. Однако реализация метода dunder_len тривиальна.
Тони Саффолк 66,
@ TonySuffolk66: верно ли, что ____len____ определяет итерабельность для индекса (этажей) в вашем примере, на котором ____getitem____ зацикливается?
Alex
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'
user3503692
источник
Отличный пример! Я долго искал, как реализовать getitem, когда в init есть несколько параметров, и я изо всех сил пытался найти правильную реализацию и наконец увидел это! Проголосовали и спасибо!
Рахул П.