簡體   English   中英

將迭代器轉換為列表的最快方法

[英]Fastest way to convert an iterator to a list

擁有iterator對象,是否有比列表理解更快、更好或更正確的方法來獲取迭代器返回的對象列表?

user_list = [user for user in user_iterator]
list(your_iterator)

python 3.5 開始,您可以使用*可迭代解包運算符:

user_list = [*your_iterator]

但是pythonic的方法是:

user_list  = list(your_iterator)

@Robino 建議添加一些有意義的測試,因此這是將迭代器轉換為列表的 3 種可能方法(可能是最常用的方法)之間的簡單基准測試:

  1. 按類型構造函數

list(my_iterator)

  1. 通過開箱

[*my_iterator]

  1. 使用列表理解

[e for e in my_iterator]

我一直在使用simple_bechmark

from simple_benchmark import BenchmarkBuilder
from heapq import nsmallest

b = BenchmarkBuilder()

@b.add_function()
def convert_by_type_constructor(size):
    list(iter(range(size)))

@b.add_function()
def convert_by_list_comprehension(size):
    [e for e in iter(range(size))]

@b.add_function()
def convert_by_unpacking(size):
    [*iter(range(size))]


@b.add_arguments('Convert an iterator to a list')
def argument_provider():
    for exp in range(2, 22):
        size = 2**exp
        yield size, size

r = b.run()
r.plot()

在此處輸入圖片說明

正如您所看到的,很難區分構造函數的轉換和解包的轉換,列表推導式的轉換是“最慢”的方法。


我還使用以下簡單腳本在不同的 Python 版本(3.6、3.7、3.8、3.9)上進行了測試:

import argparse
import timeit

parser = argparse.ArgumentParser(
    description='Test convert iterator to list')
parser.add_argument(
    '--size', help='The number of elements from iterator')

args = parser.parse_args()

size = int(args.size)
repeat_number = 10000

# do not wait too much if the size is too big
if size > 10000:
    repeat_number = 100


def test_convert_by_type_constructor():
    list(iter(range(size)))


def test_convert_by_list_comprehension():
    [e for e in iter(range(size))]


def test_convert_by_unpacking():
    [*iter(range(size))]


def get_avg_time_in_ms(func):
    avg_time = timeit.timeit(func, number=repeat_number) * 1000 / repeat_number
    return round(avg_time, 6)


funcs = [test_convert_by_type_constructor,
         test_convert_by_unpacking, test_convert_by_list_comprehension]

print(*map(get_avg_time_in_ms, funcs))

腳本將通過 Jupyter Notebook(或腳本)的子進程執行,大小參數將通過命令行參數傳遞,腳本結果將從標准輸出中獲取。

from subprocess import PIPE, run

import pandas

simple_data = {'constructor': [], 'unpacking': [], 'comprehension': [],
        'size': [], 'python version': []}


size_test = 100, 1000, 10_000, 100_000, 1_000_000
for version in ['3.6', '3.7', '3.8', '3.9']:
    print('test for python', version)
    for size in size_test:
        command = [f'python{version}', 'perf_test_convert_iterator.py', f'--size={size}']
        result = run(command, stdout=PIPE, stderr=PIPE, universal_newlines=True)
        constructor, unpacking,  comprehension = result.stdout.split()
        
        simple_data['constructor'].append(float(constructor))
        simple_data['unpacking'].append(float(unpacking))
        simple_data['comprehension'].append(float(comprehension))
        simple_data['python version'].append(version)
        simple_data['size'].append(size)

df_ = pandas.DataFrame(simple_data)
df_

在此處輸入圖片說明

你可以從這里得到我完整的筆記本。

在大多數情況下,在我的測試中,解包顯示速度更快,但差異非常小,以至於結果可能會從一次運行更改為另一次運行。 同樣,理解方法是最慢的,實際上,其他 2 種方法最多快 60%。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM