簡體   English   中英

生成器理解與開放 function

[英]Generator comprehension with open function

我試圖弄清楚在逐行解析文件時使用生成器的最佳方法是什么。 生成器理解的哪種使用會更好。

第一個選項。

with open('some_file') as file:
    lines = (line for line in file)

第二種選擇。

lines = (line for line in open('some_file'))

我知道它會產生相同的結果,但哪一個會更快/更有效?

您不能將生成器和上下文管理器( with語句)結合起來。

生成器很懶惰。 在有東西向他們請求項目之前,他們不會真正讀取他們的源數據。

似乎有效:

with open('some_file') as file:
    lines = (line for line in file)

但是當您實際上稍后在程序中嘗試讀取一行時

for line in lines:
    print(line)

它將因ValueError: I/O operation on closed file.

這是因為上下文管理器已經關閉了文件——這是它在生活中的唯一目的——並且生成器直到for循環開始實際請求數據時才開始讀取它。

你的第二個建議

lines = (line for line in open('some_file'))

遇到相反的問題。 open()文件,但除非您手動close()它(因為您不知道文件句柄而不能這樣做),否則它將永遠保持打開狀態。 這正是上下文管理器修復的情況。

總的來說,如果你想讀取文件,你可以......讀取文件:

with open('some_file') as file:
    lines = list(file)

或者您可以使用真正的生成器:

def lazy_reader(*args, **kwargs):
    with open(*args, **kwargs) as file:
        yield from file

然后你可以做

for line in lazy_reader('some_file', encoding="utf8"):
    print(line)

當讀取最后一行時, lazy_reader()將關閉文件。

如果你想測試這樣的東西,我建議查看timeit模塊。

讓我們為您的兩個測試設置一個工作版本,我將添加一些性能相同的附加選項。

這里有幾個選項:

def test1(file_path):
    with open(file_path, "r", encoding="utf-8") as file_in:
        return [line for line in file_in]

def test2(file_path):
    return [line for line in open(file_path, "r", encoding="utf-8")]

def test3(file_path):
    with open(file_path, "r", encoding="utf-8") as file_in:
        return file_in.readlines()

def test4(file_path):
    with open(file_path, "r", encoding="utf-8") as file_in:
        return list(file_in)

def test5(file_path):
    with open(file_path, "r", encoding="utf-8") as file_in:
        yield from file_in

讓我們用一個文本文件來測試它們,該文件是莎士比亞全集的 10 倍,我碰巧擁有這些文件來進行這樣的測試。

如果我做:

print(test1('shakespeare2.txt') == test2('shakespeare2.txt'))
print(test1('shakespeare2.txt') == test3('shakespeare2.txt'))
print(test1('shakespeare2.txt') == test4('shakespeare2.txt'))
print(test1('shakespeare2.txt') == list(test5('shakespeare2.txt')))

我看到所有測試都產生相同的結果。

現在讓我們計時:

import timeit

setup = '''
file_path = "shakespeare2.txt"

def test1(file_path):
    with open(file_path, "r", encoding="utf-8") as file_in:
        return [line for line in file_in]

def test2(file_path):
    return [line for line in open(file_path, "r", encoding="utf-8")]

def test3(file_path):
    with open(file_path, "r", encoding="utf-8") as file_in:
        return file_in.readlines()

def test4(file_path):
    with open(file_path, "r", encoding="utf-8") as file_in:
        return list(file_in)

def test5(file_path):
    with open(file_path, "r", encoding="utf-8") as file_in:
        yield from file_in
'''

print(timeit.timeit("test1(file_path)", setup=setup, number=100))
print(timeit.timeit("test2(file_path)", setup=setup, number=100))
print(timeit.timeit("test3(file_path)", setup=setup, number=100))
print(timeit.timeit("test4(file_path)", setup=setup, number=100))
print(timeit.timeit("list(test5(file_path))", setup=setup, number=100))

在我的筆記本電腦上,這向我展示了:

9.65
9.79
9.29
9.08
9.85

向我建議從性能角度選擇哪一個並不重要。 所以不要使用你的test2()策略:-)

請注意,盡管從 memory 管理的角度來看, test5() (歸功於@tomalak)可能很重要。

暫無
暫無

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

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