继承列表以使用它的就地排序方法

时间:2011-10-10 22:44:24

标签: python sorting mmap

我正在尝试对几个文件的内容进行排序(有时会将一行从一个文件移到另一个文件中)

我想使用内置的自适应合并排序和list的属性。我尝试从list继承该方法,但我不知道它是否需要超过__len____getitem____setitem__。是。我想要就地排序。

仅供参考,这是我的代码到目前为止(如果它有助于解释我在做什么),当我调用.sort()时,顺序不会改变。如果我添加我自己用python编写的bubble_sort方法,它可以工作,但速度非常慢:

class Memwrap(list):
    def __init__(self, prefix, folder='.', chunksize=None):

        fns = [fn for fn in os.listdir(folder) if fn.startswith(prefix)]
        fns.sort()
        self.files = [open(os.path.join(folder,fn), 'r+') for fn in fns]
        self.mmaps = [mmap.mmap(f.fileno(), 0) for f in self.files]
        self.sizes = [mm.size() for mm in self.mmaps]
        if chunksize is None:
            self.chunksize = len(self.mmaps[0].readline())
        else:
            self.chunksize = chunksize


    def _mm_from_idx(self, idx):
        bidx = self.chunksize*idx
        lo = 0
        for m,s in zip(self.mmaps, self.sizes):
            hi = lo + s
            if lo <= bidx < hi:
                return bidx-lo, m
            lo = hi

    def __getitem__(self, idx):
        bidx, mmap = self._mm_from_idx(idx)        
        return mmap[bidx:bidx+self.chunksize]

    def __setitem__(self, idx, val):
        assert len(val) == self.chunksize
        bidx, mmap = self._mm_from_idx(idx)
        mmap[bidx:bidx+self.chunksize] = val

    def __len__(self):
        assert not sum(self.sizes)%self.chunksize
        return sum(self.sizes)/self.chunksize


    def bubble_sort(self):
        for i in xrange(0, len(self) - 1):
            swap_test = False
            for j in range(0, len(self) - i - 1):
                if self[j] > self[j + 1]:
                    self[j], self[j + 1] = self[j + 1], self[j]  # swap
                swap_test = True
            if swap_test == False:
                break
        self.flush()


    def flush(self):
        for mm in self.mmaps:
            mm.flush()

    def close(self):
        self.flush()
        for mm in self.mmaps:
            mm.close()
        for f in self.files:
            f.close()

3 个答案:

答案 0 :(得分:1)

列表的排序方法是用C语言编写的(在CPython中),因此根本不需要使用__getitem__和_ _setitem__方法。如果它确实使用了这些方法,那么它会大大减慢正常的list.sort()

您可以将文件索引存储在列表中,并使用list的内置排序和key函数为您完成一些工作

答案 1 :(得分:1)

这里提供了实际的列表排序代码(第2000行):https://github.com/python-git/python/blob/master/Objects/listobject.c

据我所知,不可能将该代码用于不同的存储机制。

您可以更快地使用排序算法(使用快速排序等)或使用ctypes模块与基于C的排序算法(例如http://tomoyo.sourceforge.jp/cgi-bin/lxr/source/lib/sort.c)进行交互。

最后,您可以使用linux排序应用程序对数据进行排序(如果您希望在Python中控制它,则使用子进程模块)。 sort filea fileb filec可能比你在Python,mmap或者不能做的任何事情都要快。

答案 2 :(得分:1)

你误解了从list继承的内容。

您的Memwrap不只是让list界面 一个列表(这是一个内存结构,就像一个Python对象数组不能直接用Python表示)。然后添加一些额外的实例成员,并覆盖一些list方法与您的成员交谈而不是正常的list实例数据(因为您实际上从未调用基类实现)。请注意,Memwrap仍然具有从list继承的实例数据,但就数据而言,它仍然是一个空列表,其中添加了一堆属性。

内置类型的许多操作都是在C中实现的,只是直接使用C级数据而不是通过Python级钩子(如__getitem__)。因此,虽然这种继承到get-the-interface的接口可以用于Python级别的类(虽然它确实是一个hack),但它通常不适用于内置类型。这肯定不是你对“构建类型”的子类化“应该”做的事情;更期望你使list的工作方式略有不同(你可以添加默认值,额外方法,元数据等),而不是你做一个完全不同的共享接口的东西。为此,请参阅collections模块中的ABC。

我没有看到如何在没有list的情况下使用list的内置排序。内存映射文件应该比执行大量的操作系统级别的调用更快,但是为了排序你实际上是在读取并重写所有文件的全部内容,所以我看不出你是如何得到它的比这更快;将所有文件读入内存,对其进行排序,然后将结果写入文件。

使用mmaps执行包含固定大小数据块的就地排序文件的技巧非常聪明,但是你必须一直使用C来实际让它工作并且速度很快。 Python没有针对inplace文件排序的操作(这是一个非常模糊的操作,因为除非你假设数据块是固定大小的,否则它是不可能的),你不能得到list的排序它适合你,并且在Python中自己实现这样的排序肯定比一个好的C实现慢。也就是说,它应该是一个IO绑定操作,而不是计算绑定,所以你确定实际上可以比冒泡排序更快地完成 的工作吗?