__getitem__、__setitem__ 如何使用切片?

How do __getitem__, __setitem__, work with slices?

我是 运行 Python 2.7.10.

我需要拦截列表中的更改。 "change" 我的意思是任何在浅层意义上修改列表的东西(如果列表由相同顺序的相同对象组成,则列表不会更改,无论这些对象的状态如何;否则,它是)。我不需要找出如何 列表发生了变化,只知道它发生了变化。所以我只是确保我可以检测到它,然后让基本方法完成它的工作。这是我的测试程序:

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

案例 #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]

我对#7 并不感到非常惊讶:append 可能是以临时方式实现的。但是对于#4 和#6 我很困惑。 __getitem__ 文档说:"Called to implement evaluation of self[key]. For sequence types, the accepted keys should be integers and slice objects."(我的重点)。对于 __setitem__: " 与 __getitem__()" 相同的注释,我认为 key 也可以是一个切片。

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

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

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

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

这是解释吗?这是在说"Well, the methods are deprecated, but in order to achieve the same functionality in 2.7.10 as you had in 2.0 you still have to override them"吗? las,那您为什么不赞成使用它们?未来将如何运作?是否有 "list" class - 我不知道 - 我可以延长并且不会带来这种不便?我真正需要重写什么以确保捕获每个列表修改操作?

问题是您要对内置函数进行子类化,因此必须处理一些问题。在我深入研究这个问题之前,我将直接进入“现代”方式:

How will things work in the future? Is there a "list" class - that I am not aware of - that I could extend and would not present this inconvenience?

是的,有标准库 Abstract Base Classes. You can avoid the ugly complications caused by subclassing builtin list by using the ABCs instead. For something list-like, try subclassing MutableSequence:

from collections import MutableSequence

class MyList(MutableSequence):
    ...

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


如果您想继续对内置函数进行子类化 list,请继续阅读...

您的猜测是正确的,您需要覆盖 __getslice____setslice__language reference explains why 你已经看到了:

However, built-in types in CPython currently still implement __getslice__(). Therefore, you have to override it in derived classes when implementing slicing.

请注意,l[3:7] 将挂接到 __getslice__,而其他等价的 l[3:7:] 将挂接到 __getitem__,因此您必须处理接收切片的可能性两种方法...呻吟!