为什么当我使用 [:] 时,我的子类的 __getitem__ 和 __setitem__ 没有被调用?

Why are my subclass's __getitem__ and __setitem__ not called when I use [:]?

提问人:Aniol 提问时间:7/12/2018 最后编辑:smciAniol 更新时间:7/2/2019 访问量:1087

问:

我正在研究 Python 2.7,我正在尝试重载并从继承 .__getitem____setitem__list

假设我有这个类:A

 class A(list):
    def __getitem__(self, key):
        print "GET!"

   def __setitem__(self, key, value):
        print "SET!"        

用方括号括起来,或者应该叫。通常是这样,但是当我使用父实现时,会改为调用。为什么?为什么有效?A.__getitem__A.__setitem__[:][::]

a = A([1])
a[1] # prints GET!
a["1"] # prints GET!
a[::] # prints GET!
a[slice(None)] # prints GET!
a[:] # returns the list [1]

和一样:__setitem__

a[1] = 2 # prints SET!
a[::] = 2  # prints SET!
a[slice(None)] = 2  # prints SET!
a[:] = [2] # changes the list 
python-2.7 列表 切片 python-internals

评论


答:

10赞 MSeifert 7/12/2018 #1

这是因为在 Python 2 [1] 以及带有 start 和/或 end1-d 切片(但在指定步骤时不是这样),例如 、 ,或者如果它们被实现,则通过 __getslice____setslice__。如果它们没有实现,它们也将转到 和 )。引用文档:[:][1:][:3][1:3]__getitem____setitem__

请注意,仅当使用具有单个冒号的单个切片并且切片方法可用时,才会调用这些方法 []。对于涉及扩展切片表示法的切片操作,或者在没有切片方法的情况下,使用 slice 对象作为参数调用 。__*slice____getitem__()__setitem__()__delitem__()

在你的情况下,你继承了它们(实现它们),所以它绕过了你的和在简单的切片情况下。listlist__getitem____setitem__

例如,您可以重写这些方法以验证调用是否确实到达那里:__*slice__[:]

class A(list):
    def __getitem__(self, key):
        print "GET!"

   def __setitem__(self, key, value):
        print "SET!"  

    def __getslice__(self, i, j):
        print "GETSLICE!"

   def __setslice__(self, i, j, seq):
        print "SETSLICE!"  

但是,仅当仅传入一个切片时,并且仅当传递的切片没有步骤时,才会调用这些切片。所以不会去那里,因为它有一个步骤(即使它是隐含的)。但也不会进入这些,因为它被翻译成不是一个简单的切片,而是一元组的切片。如果您自己传入一个实例,它也不会进入这些方法,这就是为什么即使看似等同于直接进入而不是 .[::][:,:]tuple(slice(None), slice(None))__*slice__slice[slice(None)][:]__*item____*slice__


[1] 在 Python 3 中,方法被删除了,因此索引将转到方法。__*slice__[whatever]__*item__

评论

0赞 Aniol 7/12/2018
感谢您的回答,但是我不明白的情况。在键上,我收到一个不是 ,直接给[::]slice(None)tupleslicea[slice(None)]
0赞 MSeifert 7/12/2018
隐式包含一个步骤(即使它等于默认步骤),因此它不能转到 .[::]__*slice__