我已经阅读了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__
。但为什么要这样做呢?
有人可以更详细地解释一下吗?
答案 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
子类。目的是避免一次实例化一个字典,该字典要么在现有容器中已经有大量的键值对,要么在键值对的现有容器之间具有昂贵的散列过程,或者如果字典表示单个组分布在互联网上的资源。
举一个简单的例子,假设你有两个列表,keys
和 values
,其中 {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) 您无法猜测,您必须查看文档。