如何在其派生类中覆盖列表的切片功能

Posted

技术标签:

【中文标题】如何在其派生类中覆盖列表的切片功能【英文标题】:How to override the slice functionality of list in its derived class 【发布时间】:2013-04-08 14:53:15 【问题描述】:

我做了一个如下的类:

class MyList(list):
    def __init__(self, lst):
        self.list = lst

我希望在 MyList 中覆盖切片功能

【问题讨论】:

【参考方案1】:

您需要提供自定义的__getitem__()__setitem____delitem__ 钩子。

这些在切片列表时传递一个slice object;这些具有startstopstep 属性。但是,这些值可能是None,以表示默认值。请注意,当您使用负步幅时,默认值实际上会发生变化!

但是,它们也有一个slice.indices() method,当给定一个长度时,它会产生一个适合range() 对象的(start, stop, step) 值的元组。此方法处理诸如以负步幅进行切片且没有开始或停止索引等令人讨厌的细节:

def __getitem__(self, key):
    if isinstance(key, slice):
        indices = range(*key.indices(len(self.list)))
        return [self.list[i] for i in indices]
    return self.list[key]

或者,对于您的情况:

def __getitem__(self, key):
    return self.list[key]

因为list 可以直接获取slice 对象。

在 Python 2 中,list.__getslice__ 在没有跨步的情况下被调用(所以只有开始和停止索引)如果实现了,内置的list 类型实现了它,所以你也必须覆盖它;对您的 __getitem__ 方法的简单委托应该可以:

def __getslice__(self, i, j):
    return self.__getitem__(slice(i, j))

【讨论】:

绝妙的答案。 然而,应该注意的是,这是... 不完整的。 传递给__getitem__()key 可以也是一个 tuple 包含一个或多个 slice 项目,需要类似地处理。当然,__setitem__()__delitem__() dunder 方法也需要类似的泛化。有关处理所有可能的边缘情况的相关子类逻辑,请参阅this MultiIndexingList example。 @CecilCurry 我特别关注如何对 Python 列表 进行切片。标准列表类型不处理扩展切片,这就是我省略它的原因。【参考方案2】:
class MyList(list):
    def __init__(self, lst):
        self.list = lst

没有多大意义...self 是列表对象本身,并且此时它已经创建,也许您想覆盖__new__,但是您可能不需要触摸它.无论如何,您想像这样覆盖__getitem__

def __getitem__(self, val):
    if isinstance( val, slice):
        # do stuff here

【讨论】:

【参考方案3】:

正如@Martijn Pieters 和@jamylak 所说,您需要实现__getitem__ 方法。

一般来说,如果一个人想要覆盖它的超类的切片功能,并继续使用已经使用超类__getitem__的超类方法,只需将其拆分为案例,并从两个世界中受益。

例如:

import pandas as pd

class SubFrame(pd.DataFrame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def __getitem__(self, item):
        if isinstance(item, slice):
            # your implementation
            print("'My slicing'")
            return SubFrame(super().__getitem__(item))
        else:
            return super().__getitem__(item)

现在你可以这样做了:

>>> sf = SubFrame('A': (10,20,30,40,50))
>>> sf[1:-2]
'My slicing'
    A
1  20
2  30
>>> type(sf[1:-2])
<class '__main__.SubFrame'>

[] 仍然会调用super 的__getitem__

>>> sf['A']  # super's __getitem__
0    10
1    20
2    30
3    40
4    50
Name: A, dtype: int64
>>> type(sf['A'])
<class 'pandas.core.series.Series'>

【讨论】:

以上是关于如何在其派生类中覆盖列表的切片功能的主要内容,如果未能解决你的问题,请参考以下文章

抽象派生类中的抽象方法覆盖/实现抽象基类的抽象或具体方法。如何以及为啥?

如果该属性在派生类中被覆盖,如何调用基类的属性?

比较Java方法的重载与覆盖

朋友类对象可以在其成员函数中访问派生类对象上的基类私有成员吗?

为啥基类指针指向基类中的纯虚方法,而不是派生类中的覆盖方法?

C ++:如何在派生类中定义基类构造函数,如果基构造函数具有带有私有成员的初始化列表[重复]