Python:是否可以创建一个类方法对类的切片进行操作?

4 投票
1 回答
1109 浏览
提问于 2025-04-18 18:36

我正在用Python创建一个容器类,这个类要么继承自list,要么实现所有标准的列表方法(我对这两种方式都无所谓)。

我想知道怎么创建一个方法,只对切片返回的那些项目进行操作。我已经能做一个对整个容器操作的方法(见下面的代码),但我就是搞不清楚怎么只对切片进行操作。

我使用的是Python 2.7.6,并且在我的所有代码中都使用from __future__ import print_function, division

下面是一个我想要的用法示例:

from __future__ import print_function, division
import itertools

class MyContainerClass(list):
    """ For now, I'm inheriting from list. Is this my problem? """
    def __init__(self, data_array):
        list.__init__(self, data_array)

    def __getitem__(self, *args):

        arg = args[0]

        # specific indices   MyContainerClass[0, 3, 5] => indexes 0, 3, and 5
        if isinstance(arg, (list, tuple)) and not isinstance(arg[0], bool):
            return [list.__getitem__(self, _i) for _i in arg]

        # standard slice notation
        elif isinstance(arg, slice):
            return list.__getitem__(self, arg)

        # Using an array mask   MyContainerClass[[True, False, False,...,True]] => index 0 and -1]
        elif isinstance(arg[0], bool):  # or isinstance(arg, np.ndarray):
            return list(itertools.compress(self, arg))

        else:
            # I'll eventually replace this with and exception raise.
            return 'error'

    def my_method(self):
        """
        Will act on entire list, but I want it to act on only what's
        returned by the slice (which may be the entire list in some cases).
        """
        return "a, ".join([str(_i) for _i in self])

这是我想要的用法示例:

>>> data = MyContainerClass([1, 2, 3, 4, 5, 6, 7])
>>> data[5:]
[6, 7]
>>> data.my_method()           # This works as expected
"1a, 2a, 3a, 4a, 5a, 6a, 7"
>>> data[0:3].my_method()      # Doesn't work
"1a, 2a, 3"                    # but should return this


看起来现在一切都正常了。非常感谢大家!这是我想到的:

from __future__ import print_function, division
import itertools

class MyContainerClass(list):
    """
    """
    def __init__(self, array):
        if isinstance(array, int):
            list.__init__(self, [array])
        else:
            list.__init__(self, array)

    def __getitem__(self, arg):
        # Standard Slice notation
        if isinstance(arg, slice):
            retval = super(MyContainerClass, self).__getitem__(arg)

        # specific indices
        elif isinstance(arg, (list, tuple)) and not isinstance(arg[0], bool):
            retval = [list.__getitem__(self, _i) for _i in arg]

        # a single specific index
        elif isinstance(arg, int):
            retval = list.__getitem__(self, arg)

        # an array mask of T/F values
        elif isinstance(arg[0], bool):      # or isinstance(arg, np.ndarray):
            retval = list(itertools.compress(self, arg))

        # raise an error on unknown
        else:
            raise SyntaxError("Unknown notation for list slice or index")

        retval = type(self)(retval)
        return retval

    def __getslice__(self, i, j):
        # Python 2 built-in types only
        return self.__getitem__(slice(i, j))

    def my_method(self):
        return "a, ".join([str(_i) for _i in self])

并且表现得像:

>>> data = MyContainerClass([1, 2, 3, 4, 5, 6, 7])
>>> mask = [True, True, False, True, False, False, False]
>>> print(data)
[1, 2, 3, 4, 5, 6, 7]
>>> print(type(data[5:]))
<class '__main__.MyContainerClass'>
>>> print(data.my_method())
1a, 2a, 3a, 4a, 5a, 6a, 7
>>> print(data[0:5].my_method())
1a, 2a, 3a, 4a, 5
>>> print(data[1, 5, 2].my_method())
2a, 6a, 3
>>> print(data[mask].my_method())
1a, 2a, 4
>>> print(data[2].my_method())
3

1 个回答

8

你需要确保你的 __getitem__ 方法返回的是你想要的类型:

class MyContainerClass(list):
    """ For now, I'm inheriting from list. Is this my problem? """
    def __init__(self, data_array):
        list.__init__(self, data_array)

    def my_method(self):
        """
        Will act on entire list, but I want it to act on only what's
        returned by the slice (which may be the entire list in some cases).
        """
        return "a, ".join([str(_i) for _i in self])

    def __getitem__(self, index):
        retval = super(MyContainerClass, self).__getitem__(index)
        if isinstance(index, slice):
            retval = type(self)(retval)
        return retval

    def __getslice__(self, i, j):
        # Python 2 built-in types only
        return self.__getitem__(slice(i, j))

额外的 __getslice__ 方法 只在 Python 2 中需要,而且只有在你继承自一个已经实现了 __getslice__ 的类型时才需要。比如说,list 就是这样一个类型。

示例:

>>> data = MyContainerClass([1, 2, 3, 4, 5, 6, 7])
>>> data[:5]
[1, 2, 3, 4, 5]
>>> type(data[:5])
<class '__main__.MyContainerClass'>
>>> data.my_method()
'1a, 2a, 3a, 4a, 5a, 6a, 7'
>>> data[:3].my_method()
'1a, 2a, 3'

撰写回答