Python - 就地列表分区

时间:2017-04-14 12:49:05

标签: python

有没有办法使用标准的python 2.7(c ++ STL std :: partition样式)按谓词分区列表?来自group_by的{​​{1}}之类的内容,但没有创建其他数组?我需要根据可变参数条件递归地将数组分成两组,并且我受到RAM数量的限制。

我正在寻找的功能如下:

itertools

这将导致partitionCPPStyle(data, startIndex, endIndex, condition)列表中所有元素满足开头条件并返回未满足条件的第一个元素的索引。没有副本,因为尽可能少使用额外的内存。

我最终编写了自己的实现:

data[startIndex:endIndex]

有更有效的方法吗?

1 个答案:

答案 0 :(得分:0)

这相对容易实现 - 但是因为你有"条件" (我将使用术语"谓词"从这里开始)有一个复杂的问题:因为根本没有复制,所得结构的唯一方法是“#34;知道"如果参与特定谓词的项目是在访问时检查它 - 这意味着你将有"漏洞"在您的索引中。

给出一个例子,这更容易理解:

a = list(range(20))
b = SlicedList(a, slice(10, 20), predicate=lambda x: x%2
len(b) # will correctly report (5)
b[0] # will raise ValueError as "10" fails the predicate
# so, 0-9 are valid indexes for "b", but only the contents 
# that attend the predicate will actually return a value
# you can safely iterate on b with a "for", though:
for item in b:
    print(item)  # (11, 13, 15, 17, 19)

但是,对于迭代,它应该可以很好地工作。

try:
    from collections.abc import MutableSequence
except ImportError:
    from collections import MutableSequence


class SlicedList(MutableSequence):
    def __init__(self, data, slice=None, predicate=None):
        self.data = data
        if not slice:
            slice = __builtins__.slice(0, len(data), 1)
        self.slice = slice
        self.predicate = predicate

    def __getitem__(self, index):
        if index < 0:
            raise NotImplementedError("Can't use negative indexes on Sliced lists")
        real_index = self.slice.start + index * (self.slice.step or 1)
        item = self.data[real_index]
        if self.predicate and not self.predicate(item):
            raise ValueError("Item at position {} does not attend the predicate".format(index))
        return item

    def __setitem__(self, index, value):
        ...

    def __delitem__(self, index):
        ...

    def __len__(self):
        if not self.predicate:
            start, stop, step = self.slice.indices(len(data))
            return (stop - start) // (step or 1)
        count = 0
        for i in range(self.slice.start, self.slice.stop, self.slice.step or 1):
            if self.predicate(self.data[i]):
                count += 1
        return count

    def __iter__(self):
        for i in range(self.slice.start, self.slice.stop, self.slice.step or 1):
            value =self.data[i]
            if not self.predicate or self.predicate(value):
                yield i

    def insert(self, position, value):
        ...

另一个暗示是尽快退出Python 2.7--所有现代库和框架在Python 3上都运行正常,而Python 2现在变得非常老化。下面的代码将适用于两者,但我必须为它做好准备。