__getitem __,__ setitem__如何处理切片?

时间:2015-09-24 23:56:05

标签: python

我正在运行Python 2.7.10。

我需要拦截列表中的更改。通过"改变"我的意思是在浅层意义上修改列表的任何内容(如果列表由相同顺序的相同对象组成,则不会更改列表,无论这些对象的状态如何;否则,它是)。我不需要找出如何列表已更改,只有它已经更改。所以我只是确保我可以检测到它,并让base方法完成它的工作。这是我的测试程序:

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__文档说:"被调用来实现对self [key]的评估。对于序列类型,接受的键应该是整数和切片对象。" (我的强调)。对于__setitem__"与__getitem__()" 相同,我认为key也可以是切片。

我的推理出了什么问题?如果有必要的话,我已经准备好覆盖每个列表修改方法(追加,扩展,插入,弹出等),但应该覆盖什么才能捕获类似#6的东西?

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

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

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

这是解释吗?这是谚语"嗯,方法 已弃用,但为了在2.7.10中实现与2.0中相同的功能,您仍然需要覆盖它们"?唉,那你为什么要弃他们呢?将来如何运作?有没有"列表"课程 - 我不知道 - 我可以延长并且不会带来这种不便吗?我真的需要覆盖什么来确保我捕获每个列表修改操作?

1 个答案:

答案 0 :(得分:4)

你的问题是你是一个内置的子类,所以必须处理一些皱纹。在我深入研究这个问题之前,我会直接回答“更好”的答案:

  

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

是的,现代的方法是使用python的Abstract Base Classes。通过使用ABCs,您可以避免在使用内置list进行子类化时看到的这些丑陋的复杂情况。对于类似列表的内容,请尝试继承MutableSequence

from collections import MutableSequence

class MyList(MutableSequence):
    ...

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

如果您想继续推进内置list的子类化,请继续阅读...

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

  

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

请注意,l[3:7]会挂钩到__getslice__,而等效的l[3:7:]会挂钩__getitem__。所以你必须处理两片......呻吟!