覆盖python中的元组.__ len__

时间:2014-05-15 08:45:07

标签: python python-2.7 inheritance slice

我想本地在python中实现一个不可变类型。我从这里收集了

How to make an immutable object in Python?

最好的方法是覆盖元组。让我们说我想要一个存储一些额外数据的元组。我会做类似的事情:

class MyTuple(tuple):
    def __new__(cls, lst, data):
        return tuple.__new__(cls, tuple(lst) + (data, ))

现在我想覆盖len函数:

    def __len__(self):
        return tuple.__len__(self) - 1

我的python版本在家里工作得很好(Python 2.something,但我不知道哪个),但是在我的办公室计算机上(Python 2.7.3)它破坏了切片:如果我做

m = MyTuple([1,2], 0)
print(m[:-1])

我在家里得到(1, )我会得到(1, 2)(我实际上没有检查过,但我将问题追溯到这个最小的例子,我想是这样)。所以在一个实现中,切片调用tuple.__len__,而另一个实现调用MyTuple.__len__。我不会介意,但一致性会很好。

所以我的问题是:是否有一个强大的解决方案,如果没有,哪个行为将是稳定的?

编辑:正如Leonardo.Z建议的那样,我的家庭" python实际上是Python 3(.4)。我一直在尝试一个新的ide,而没有注意它使用的是哪个Python。

2 个答案:

答案 0 :(得分:1)

切片不一定与调用__len__()有关,尤其不适用于内置类型。

m[:-1]转换为m.__getitem__(slice(None, -1)),可以通过调用内部长度函数以一种非常简单的方式处理tuple类型,这可以忽略__len__的事实重写的。

Here是函数在内部定义的方式。

它似乎广泛使用PyTuple_GET_SIZE()。我没有找到它的定义,但我认为它的定义非常简单。

答案 1 :(得分:1)

它是由Python2和Python3的切片实现之间的差异引起的。

在Python3中,obj[x:y]始终等于obj.__getitem__(slice(x,y))。但是在Python2中,由于历史原因,一些内置类型(包括元组)使用__getslice__方法实现了切片运算符。

__getslice__使用__len__方法,__getitem__没有。

只需在Python2和Python3中运行以下代码,您就会明白为什么。

from __future__ import print_function

class MyTuple(tuple):
    def __new__(cls, lst, data):
        return tuple.__new__(cls, tuple(lst) + (data, ))
    def __len__(self):
        return tuple.__len__(self) - 1

    def __getitem__(self, key):
        print("__getitem__() is called.  start:%s, end:%s" % (key.start, key.stop))
        return super(MyTuple, self).__getitem__(key)

    def __getslice__(self, start, end):
        print("__getslice__() is called.  start:%s, end:%s" % (start,end))
        return super(MyTuple, self).__getslice__(start,end)

m = MyTuple([1,2], 0)

print(m[:-1])

根据Python官方文档,当您在子类中更改__getslice__()实现时,应该覆盖__len__方法。 参考:

The official doc about object.__getslice__.