【问题标题】:Python: Is is possible to have a class method that acts on a slice of the class?Python:是否有可能有一个作用于类切片的类方法?
【发布时间】:2014-10-20 02:38:51
【问题描述】:

我正在 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

【问题讨论】:

  • 切片会再次成为该类的实例吗?如果您只是从list 继承该方法,那么可能不会。这就是问题所在:切片是list,它没有这个方法。或者,为什么不把它变成一个函数而不是一个方法呢?
  • 也许你会感兴趣:stackoverflow.com/a/16033058/2363712 ?
  • 这是 Python 2 还是 Python 3?
  • @tobias_k:我希望切片成为该类的一个实例,是的。那应该可以解决我的问题。至于函数v法:在大项目设计中更有用的方法。你在这里看到的只是一个简化的例子:-)
  • @SylvainLeroux:我正在编写的实际类使用 getitem 以便我可以接受切片表示法、数组掩码或索引列表来返回元素。正如我在另一条评论中提到的,似乎我只需要弄清楚如何让 getitem 返回 MyContainerClass 的实例而不是列表。

标签: python list slice


【解决方案1】:

您必须确保您的 __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))

仅在 Python 2 中需要额外的 __getslice__ method,并且仅当您从已经实现 __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'

【讨论】:

  • 我不熟悉这行:retval = type(self)(retval)。你能解释一下吗?我之前使用过 type(item),但之后没有使用第二组 ()。
  • @dthor:我让你的类成为子类; type(self) 返回 MyContainerClass(或任何子类),因此您可以再次调用该对象来创建新实例。
  • @dthor:您可以使用retval = MyContainerClass(retval) 对类进行硬编码,但子类必须覆盖__getitem__ 才能更改返回的类型。
  • @dthor 如果这让您感到困惑,您可以将其替换为 self.__class__(retval)。它们是同义词。获取self的类,并使用其构造函数返回一个新对象。
  • @AdamSmith 谢谢。我从 MartijnPieters 评论中理解了这一点,但您的评论让我意识到它类似于通过 self.__class__ = NewClass 设置实例的 __class__ (我在代码库的其他地方执行此操作)。我知道这不一样,因为我们在这里创建了一个新对象。
猜你喜欢
  • 2018-10-09
  • 1970-01-01
  • 2014-06-06
  • 1970-01-01
  • 2020-07-23
  • 1970-01-01
  • 2011-01-11
  • 2014-12-14
  • 2016-03-18
相关资源
最近更新 更多