在Python中展开浅层列表

这个问题在这里已经有了答案:

  • 在Python 31列表中列出一个扁平列表的答案

  • 如果您只是在遍历数据结构的扁平版本并且不需要可索引序列,请考虑itertools.chain和company。

    >>> list_of_menuitems = [['image00', 'image01'], ['image10'], []]
    >>> import itertools
    >>> chain = itertools.chain(*list_of_menuitems)
    >>> print(list(chain))
    ['image00', 'image01', 'image10']
    

    它将处理任何可迭代的事物,其中应包括Django的可迭代QuerySet ,它似乎是您在问题中使用的。

    编辑:这可能与减少一样好,因为减少将有相同的开销将项目复制到正在扩展的列表中。 chain只会在最后运行list(chain)时产生此(相同)开销。

    Meta-Edit:实际上,这比问题提出的解决方案花费更少,因为当您使用临时扩展原始文件时,会丢弃您创建的临时列表。

    编辑:由于JF塞巴斯蒂安说itertools.chain.from_iterable避免拆包,你应该使用它来避免*魔法,但timeit应用程序显示可以忽略不计的性能差异。


    你几乎拥有它! 执行嵌套列表解析的方法是将for语句按照它们在常规嵌套for语句中的顺序for

    因此,这

    for inner_list in outer_list:
        for item in inner_list:
            ...
    

    对应于

    [... for inner_list in outer_list for item in inner_list]
    

    所以你要

    [image for menuitem in list_of_menuitems for image in menuitem]
    

    @ S.Lott:你激励我写一个timeit应用程序。

    我想它也会根据分区数量(容器列表中的迭代器数量)而有所不同 - 您的评论没有提及这30个项目有多少个分区。 这种情节在每次运行中都会压扁一千个项目,分区数量也不尽相同。 这些项目均匀分布在分区之间。

    展平比较

    代码(Python 2.6):

    #!/usr/bin/env python2.6
    
    """Usage: %prog item_count"""
    
    from __future__ import print_function
    
    import collections
    import itertools
    import operator
    from timeit import Timer
    import sys
    
    import matplotlib.pyplot as pyplot
    
    def itertools_flatten(iter_lst):
        return list(itertools.chain(*iter_lst))
    
    def itertools_iterable_flatten(iter_iter):
        return list(itertools.chain.from_iterable(iter_iter))
    
    def reduce_flatten(iter_lst):
        return reduce(operator.add, map(list, iter_lst))
    
    def reduce_lambda_flatten(iter_lst):
        return reduce(operator.add, map(lambda x: list(x), [i for i in iter_lst]))
    
    def comprehension_flatten(iter_lst):
        return list(item for iter_ in iter_lst for item in iter_)
    
    METHODS = ['itertools', 'itertools_iterable', 'reduce', 'reduce_lambda',
               'comprehension']
    
    def _time_test_assert(iter_lst):
        """Make sure all methods produce an equivalent value.
        :raise AssertionError: On any non-equivalent value."""
        callables = (globals()[method + '_flatten'] for method in METHODS)
        results = [callable(iter_lst) for callable in callables]
        if not all(result == results[0] for result in results[1:]):
            raise AssertionError
    
    def time_test(partition_count, item_count_per_partition, test_count=10000):
        """Run flatten methods on a list of :param:`partition_count` iterables.
        Normalize results over :param:`test_count` runs.
        :return: Mapping from method to (normalized) microseconds per pass.
        """
        iter_lst = [[dict()] * item_count_per_partition] * partition_count
        print('Partition count:    ', partition_count)
        print('Items per partition:', item_count_per_partition)
        _time_test_assert(iter_lst)
        test_str = 'flatten(%r)' % iter_lst
        result_by_method = {}
        for method in METHODS:
            setup_str = 'from test import %s_flatten as flatten' % method
            t = Timer(test_str, setup_str)
            per_pass = test_count * t.timeit(number=test_count) / test_count
            print('%20s: %.2f usec/pass' % (method, per_pass))
            result_by_method[method] = per_pass
        return result_by_method
    
    if __name__ == '__main__':
        if len(sys.argv) != 2:
            raise ValueError('Need a number of items to flatten')
        item_count = int(sys.argv[1])
        partition_counts = []
        pass_times_by_method = collections.defaultdict(list)
        for partition_count in xrange(1, item_count):
            if item_count % partition_count != 0:
                continue
            items_per_partition = item_count / partition_count
            result_by_method = time_test(partition_count, items_per_partition)
            partition_counts.append(partition_count)
            for method, result in result_by_method.iteritems():
                pass_times_by_method[method].append(result)
        for method, pass_times in pass_times_by_method.iteritems():
            pyplot.plot(partition_counts, pass_times, label=method)
        pyplot.legend()
        pyplot.title('Flattening Comparison for %d Items' % item_count)
        pyplot.xlabel('Number of Partitions')
        pyplot.ylabel('Microseconds')
        pyplot.show()
    

    编辑:决定让它成为社区wiki。

    注意: METHODS应该可以与装饰器一起累积,但我认为人们用这种方式阅读会更容易。

    链接地址: http://www.djcxy.com/p/69969.html

    上一篇: Flattening a shallow list in Python

    下一篇: Read specific sequence of lines in Python