【问题标题】:Overriding tuple.__len__ in python在 python 中覆盖 tuple.__len__
【发布时间】:2014-07-03 14:38:29
【问题描述】:

我想在 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。

【问题讨论】:

  • 我确定你家的Python版本是3,而不是2!

标签: python python-2.7 inheritance slice


【解决方案1】:

切片不一定与调用__len__() 相关,尤其是与内置类型无关。

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

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

它似乎广泛使用了PyTuple_GET_SIZE()。我没有找到它的定义,但我想它的定义很简单。

【讨论】:

  • PyTuple_GET_SIZEtupleobject.h 中定义为Py_SIZE 的别名,它读取PyObject_VAR_HEAD struct 中的标准ob_size 条目。
  • @MartijnPieters 谢谢,现在我找到了它,所以它确实完全绕过了调用__len__()
【解决方案2】:

这是由于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 官方文档,当您更改子类中的 __len__ 实现时,您应该覆盖 __getslice__() 方法。 参考:

The official doc about object.__getslice__.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-01
    • 2015-09-07
    • 1970-01-01
    • 2015-01-20
    • 2020-03-25
    • 1970-01-01
    • 1970-01-01
    • 2016-02-01
    相关资源
    最近更新 更多