Compreendendo o método __getitem__


138

Passei pela maior parte da documentação dos documentos do __getitem__Python, mas ainda não consigo entender o significado dela.

Então, tudo o que consigo entender é que __getitem__é usado para implementar chamadas como self[key]. Mas qual é a utilidade disso?

Digamos que eu tenho uma classe python definida desta maneira:

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

Isso retorna os resultados conforme o esperado. Mas por que usar __getitem__em primeiro lugar? Também ouvi dizer que o Python chama __getitem__internamente. Mas por que isso acontece?

Alguém pode explicar isso com mais detalhes?


Isto pode ser de interesse para um exemplo de uso: Como corretamente subclasse dict e override getitem & SetItem
roganjosh

4
O __getitem__uso no seu exemplo não faz muito sentido, mas imagine que você precise escrever uma classe personalizada de lista ou dicionário, que precisa trabalhar com o código existente que o usa []. Essa é uma situação em que __getitem__é útil.
Pieter Witvoet

Respostas:


157

Cong Ma faz um bom trabalho ao explicar para que __getitem__serve - mas quero dar um exemplo que pode ser útil. Imagine uma classe que modela um edifício. Dentro dos dados para o edifício, inclui vários atributos, incluindo descrições das empresas que ocupam cada andar:

Sem usar __getitem__, teríamos uma classe como esta:

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

No entanto, poderíamos usar __getitem__(e sua contrapartida __setitem__) para tornar o uso da classe Building 'mais agradável'.

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

Se você usa __setitem__esse método realmente depende de como planeja abstrair seus dados - nesse caso, decidimos tratar um edifício como um contêiner de pisos (e você também pode implementar um iterador para o edifício, e talvez até a capacidade de dividir - ou seja, obtenha mais de um andar de dados por vez - depende do que você precisa.


15
Apenas para compartilhar algo que aprendi apenas depois de ler a resposta várias vezes: depois de obter um item , não é necessário chamar explicitamente essa função. Quando ele chama, chama building1[2]a si próprio internamente, chama o getitem. Portanto, o argumento @ tony-suffolk-66 está fazendo é que, qualquer propriedade / variável da classe pode ser recuperada durante o tempo de execução, simplesmente chamando objectname [nome da variável]. Apenas esclarecendo isso, pois inicialmente não estava claro para mim e escrevendo aqui, esperando que ajude alguém. Exclua se redundante, por favor
mithunpaul 11/01/19

3
@mithunpaul a notação de objeto [index] não é usada para obter uma propriedade / variável / atributo de uma classe - ela está indexando em um objeto contêiner - por exemplo, recuperando um objeto filho de um pai onde o pai mantém uma lista de seus filhos. No meu exemplo - a classe Building é um contêiner (neste caso, nomes de Floor), mas poderia ser uma classe de contêiner para as classes Floor.
Tony Suffolk 66

Exceto que ele não suporta len(), e você receberá um TypeError:TypeError: object of type 'Building' has no len()
Ciasto piekarz

O suporte ao len (e outros recursos, como iteração etc.) não era o objetivo do meu exemplo. A implementação de um método dunder_len é trivial.
Tony Suffolk 66

@ TonySuffolk66: está correto que ____len____ determine o iterável para o índice (pisos) no seu exemplo no qual ____getitem____ faz um loop?
Alex

73

A []sintaxe para obter itens por chave ou índice é apenas açúcar de sintaxe.

Quando você avalia a[i]chamadas em Python a.__getitem__(i)(ou type(a).__getitem__(a, i), mas essa distinção é sobre modelos de herança e não é importante aqui). Mesmo que a classe de anão possa definir explicitamente esse método, ele geralmente é herdado de uma classe ancestral.

Todos os nomes de métodos especiais (Python 2.7) e suas semânticas estão listados aqui: https://docs.python.org/2.7/reference/datamodel.html#special-method-names


8

O método mágico __getitem__é basicamente usado para acessar itens de lista, entradas de dicionário, elementos de matriz etc. É muito útil para uma rápida pesquisa de atributos de instância.

Aqui estou mostrando isso com uma classe de exemplo Pessoa que pode ser instanciada por 'nome', 'idade' e 'dob' (data de nascimento). O __getitem__método é escrito de uma maneira que se pode acessar os atributos da instância indexada, como nome ou sobrenome, dia, mês ou ano do dob, etc.

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

Suponha que uma entrada do usuário seja a seguinte:

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

Com a ajuda do __getitem__método, o usuário pode acessar os atributos indexados. por exemplo,

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

Ótimo exemplo! Eu estava pesquisando por toda parte sobre como implementar o getitem quando existem vários parâmetros no init e estava lutando para encontrar uma implementação adequada e finalmente vi isso! Votado e obrigado!
Rahul P
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.