__getitem__、__setitem__ 如何处理切片?

Edu*_*rdo 3 python

我正在运行 Python 2.7.10。

我需要拦截列表中的更改。我所说的“更改”是指在浅层意义上修改列表的任何内容(如果列表由相同顺序的相同对象组成,则不会更改,而不管这些对象的状态如何;否则,则更改)。我不需要找出列表是如何改变的,只需要知道它已经改变了。所以我只是确保我可以检测到它,并让基本方法完成它的工作。这是我的测试程序:

class List(list):
    def __init__(self, data):
        list.__init__(self, data)
        print '__init__(', data, '):', self

    def __getitem__(self, key):
        print 'calling __getitem__(', self, ',', key, ')',
        r = list.__getitem__(self, key)
        print '-->', r
        return r

    def __setitem__(self, key, data):
        print 'before __setitem__:', self
        list.__setitem__(self, key, data)
        print 'after  __setitem__(', key, ',', data, '):', self

    def __delitem__(self, key):
        print 'before __delitem__:', self
        list.__delitem__(self, key)
        print 'after  __delitem__(', key, '):', self

l = List([0,1,2,3,4,5,6,7]) #1
x = l[5]                    #2
l[3] = 33                   #3
x = l[3:7]                  #4
del l[3]                    #5
l[0:4]=[55,66,77,88]        #6
l.append(8)                 #7
Run Code Online (Sandbox Code Playgroud)

案例#1、#2、#3 和#5 按我的预期工作;#4、#6 和 #7 没有。该程序打印:

__init__( [0, 1, 2, 3, 4, 5, 6, 7] ): [0, 1, 2, 3, 4, 5, 6, 7]
calling __getitem__( [0, 1, 2, 3, 4, 5, 6, 7] , 5 ) --> 5
before __setitem__: [0, 1, 2, 3, 4, 5, 6, 7]
after  __setitem__( 3 , 33 ): [0, 1, 2, 33, 4, 5, 6, 7]
before __delitem__: [0, 1, 2, 33, 4, 5, 6, 7]
after  __delitem__( 3 ): [0, 1, 2, 4, 5, 6, 7]
Run Code Online (Sandbox Code Playgroud)

我对#7 并不感到非常惊讶:append可能以一种特别的方式实现。但是对于#4 和#6 我很困惑。该__getitem__文件说:“叫实行自行[关键]的评价对于序列类型,可接受的键应该是整数和。的对象。” (我的重点)。而 for __setitem__“与 for __getitem__()相同的注释”,我认为这key也可以是一个切片。

我的推理有什么问题?如有必要,我准备覆盖每个列表修改方法(附加、扩展、插入、弹出等),但是应该覆盖什么来捕获类似 #6 的内容?

我知道 等的存在__setslice__。但这些方法自 2.0 以来已被弃用......

嗯。我再次阅读了, 等的文档__getslice____setslice__我发现这个令人毛骨悚然的声明:

“(但是,CPython 中的内置类型目前仍然实现__getslice__()。因此,在实现切片时,您必须在派生类中覆盖它。)”

这是解释吗?这是说“好吧,这些方法已被弃用,但为了在 2.7.10 中实现与 2.0 中相同的功能,您仍然必须覆盖它们”?唉,那你为什么要弃用它们呢?未来将如何运作?是否有一个“列表”类 - 我不知道 - 我可以扩展并且不会带来这种不便?我真正需要覆盖什么才能确保我捕获每个列表修改操作?

wim*_*wim 5

您的问题是您正在继承一个内置函数,因此必须处理一些问题。在我深入研究这个问题之前,我将直接进入“更好”的答案:

未来将如何运作?是否有一个“列表”类 - 我不知道 - 我可以扩展并且不会带来这种不便?

是的,现代方法是使用 python 的Abstract Base Classes。您可以list通过使用 ABC 来避免子类化 builtin 时看到的这些丑陋的并发症。对于类似列表的东西,请尝试子类化MutableSequence

from collections import MutableSequence

class MyList(MutableSequence):
    ...
Run Code Online (Sandbox Code Playgroud)

现在你应该只需要处理__getitem__和朋友的切片行为。


如果您想继续对 builtin 进行子类化list,请继续阅读...

您的猜测是正确的,您需要覆盖__getslice____setslice__。该语言参考解释了为什么你已经看到了:

但是,CPython 中的内置类型目前仍然实现__getslice__(). 因此,在实现切片时,您必须在派生类中覆盖它。

请注意,l[3:7]将挂钩到__getslice__,而其他等效项l[3:7:]将挂钩到__getitem__。所以你必须同时处理切片......呻吟!