De __getitem__-methode begrijpen

Ik heb de meeste documentatie van __getitem__in de Python-documenten doorgenomen, maar ik kan de betekenis ervan nog steeds niet begrijpen.

Het enige wat ik dus kan begrijpen is dat __getitem__wordt gebruikt om aanroepen als self[key]te implementeren. Maar wat is het nut ervan?

Laten we zeggen dat ik een python-klasse op deze manier heb gedefinieerd:

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

Dit geeft de resultaten zoals verwacht. Maar waarom zou je in de eerste plaats __getitem__gebruiken? Ik heb ook gehoord dat Python __getitem__intern aanroept. Maar waarom doet het het?

Kan iemand dit in meer detail uitleggen?


Antwoord 1, autoriteit 100%

Cong Ma legt goed uit waar __getitem__voor wordt gebruikt – maar ik wil je een voorbeeld geven dat nuttig kan zijn.
Stel je een klas voor die een gebouw modelleert. Binnen de gegevens voor het gebouw bevat het een aantal attributen, inclusief beschrijvingen van de bedrijven die elke verdieping bezetten:

Zonder __getitem__zouden we een klasse als deze hebben:

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

We zouden echter __getitem__(en zijn tegenhanger __setitem__) kunnen gebruiken om het gebruik van de klasse Building ‘leuker’ te maken.

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

Of u __setitem__op deze manier gebruikt, hangt er echt van af hoe u uw gegevens wilt abstraheren – in dit geval hebben we besloten een gebouw te behandelen als een container met verdiepingen (en u kunt ook een iterator implementeren voor het gebouw, en misschien zelfs de mogelijkheid om te slicen – dwz de gegevens van meer dan één verdieping tegelijk te krijgen – hangt af van wat u nodig heeft.


Antwoord 2, autoriteit 44%

De []-syntaxis voor het verkrijgen van item op sleutel of index is gewoon syntaxissuiker.

Als je a[i]evalueert, roept Python a.__getitem__(i)aan (of type(a).__getitem__(a, i), maar dit onderscheid gaat over overervingsmodellen en is hier niet belangrijk). Zelfs als de klasse van adeze methode niet expliciet definieert, wordt deze meestal geërfd van een voorouderklasse.

Alle (Python 2.7) namen van speciale methoden en hun semantiek worden hier vermeld: https://docs.python.org/2.7/reference/datamodel.html#special-method-names


Antwoord 3, autoriteit 3%

De magische methode __getitem__wordt in principe gebruikt voor toegang tot lijstitems, woordenboekitems, array-elementen enz. Het is erg handig voor het snel opzoeken van instantieattributen.

Hier laat ik dit zien met een voorbeeldklasse Persoon die kan worden geïnstantieerd door ‘naam’, ‘leeftijd’ en ‘geboortedatum’ (geboortedatum). De methode __getitem__is zo geschreven dat men toegang heeft tot de geïndexeerde instantiekenmerken, zoals voor- of achternaam, dag, maand of jaar van de dob, enz.

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

Stel dat de invoer van één gebruiker als volgt is:

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

Met behulp van de __getitem__methode heeft de gebruiker toegang tot de geïndexeerde attributen. bijv.

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

Antwoord 4

__getitem__kan worden gebruikt om “luie” dictsubklassen te implementeren. Het doel is om te voorkomen dat er in één keer een woordenboek wordt aangemaakt dat ofwel al een buitensporig groot aantal sleutel-waardeparen in bestaande containers heeft, of een duur hashproces heeft tussen bestaande containers met sleutel-waardeparen, of als het woordenboek een enkele groep vertegenwoordigt bronnen die via internet worden verspreid.

Als een eenvoudig voorbeeld, stel dat je twee lijsten hebt, keysen values, waarbij {k:v for k,v in zip(keys, values)}is het woordenboek dat je nodig hebt, dat lui moet worden gemaakt voor snelheids- of efficiëntiedoeleinden:

class LazyDict(dict):
    def __init__(self, keys, values):
        self.keys = keys
        self.values = values
        super().__init__()
    def __getitem__(self, key):
        if key not in self:
            try:
                i = self.keys.index(key)
                self.__setitem__(self.keys.pop(i), self.values.pop(i))
            except ValueError, IndexError:
                raise KeyError("No such key-value pair!!")
        return super().__getitem__(key)

Gebruik:

>>> a = [1,2,3,4]
>>> b = [1,2,2,3]
>>> c = LazyDict(a,b)
>>> c[1]
1
>>> c[4]
3
>>> c[2]
2
>>> c[3]
2
>>> d = LazyDict(a,b)
>>> d.items()
dict_items([])

Antwoord 5

Voor leesbaarheiden consistentie. Die vraag maakt deel uit van waarom overbelasting van operatorsbestaat, aangezien __getitem__een van de functies is die dat implementeren.

Als je een onbekende klasse krijgt, geschreven door een onbekende auteur, en je wilt het 3e element toevoegen aan het 5e element, dan kun je heel goed aannemen dat obj[3] + obj[5]zal werken.

Hoe zou die regel eruit zien in een taal die overbelasting door operators niet ondersteunt? Waarschijnlijk zoiets als obj.get(3).add(obj.get(5))?? Of misschien obj.index(3).plus(obj.index(5))??

Het probleem met de tweede benadering is dat (1) deze veel minder leesbaar is en (2) je niet kunt raden, je moetde documentatie opzoeken.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Other episodes