了解__getitem__方法

时间:2017-04-26 07:15:31

标签: python

我已经阅读了Python文档中的__getitem__的大部分文档以及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__。但为什么要这样做呢?

有人可以更详细地解释一下吗?

5 个答案:

答案 0 :(得分:69)

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__这样做取决于您计划如何抽象数据 - 在这种情况下,我们决定将建筑物视为地板容器(您也可以为建筑物实施迭代器)甚至可能切片 - 即一次获得多个楼层的数据 - 这取决于你需要的东西。

答案 1 :(得分:32)

按键或索引获取项目的[]语法只是语法糖。

当您评估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

答案 2 :(得分:1)

魔术方法__getitem__基本上用于访问列表项,字典条目,数组元素等。它对于快速查找实例属性非常有用。

在这里,我将通过示例类Person展示此示例,该类可以通过'name','age'和'dob'(出生日期)实例化。 __getitem__方法的编写方式使人们可以访问已索引实例的属性,例如名字或姓氏,日期,日期,月份或年份等。

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'

答案 3 :(得分:0)

__getitem__ 可用于实现“惰性”dict 子类。目的是避免一次实例化一个字典,该字典要么在现有容器中已经有大量的键值对,要么在键值对的现有容器之间具有昂贵的散列过程,或者如果字典表示单个组分布在互联网上的资源。

举一个简单的例子,假设你有两个列表,keysvalues,其中 {k:v for k,v in zip(keys, values)} 是你需要的字典,为了速度或效率的目的,必须将它设为惰性:

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)

用法:

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

答案 4 :(得分:0)

为了可读性一致性。这个问题是为什么运算符重载存在的一部分,因为 __getitem__ 是实现它的函数之一。

如果您得到一个未知类,由未知作者编写,并且您想将其第 3 个元素添加到其第 5 个元素中,您可以很好地假设 obj[3] + obj[5] 会起作用。

在不支持运算符重载的语言中,该行会是什么样子??可能像 obj.get(3).add(obj.get(5)) 之类的??或者obj.index(3).plus(obj.index(5))??

第二种方法的问题在于 (1) 它的可读性要低得多,并且 (2) 您无法猜测,您必须查看文档。