簡體   English   中英

Python生成器與列表理解沖突

[英]Python generator conflicting with list comprehension

我一直在使用生成器函數搞亂Python。 我想編寫一個函數,它接受一個值為元組的生成器,並返回一個生成器列表,其中每個生成器的值對應於原始元組中的一個索引。

目前,我有一個函數可以實現元組中硬編碼元素的數量。 這是我的代碼:

import itertools

def tee_pieces(generator):
    copies = itertools.tee(generator)
    dropped_copies = [(x[0] for x in copies[0]), (x[1] for x in copies[1])]
    # dropped_copies = [(x[i] for x in copies[i]) for i in range(2)]
    return dropped_copies

def gen_words():
    for i in "Hello, my name is Fred!".split():
        yield i

def split_words(words):
    for word in words:
        yield (word[:len(word)//2], word[len(word)//2:])

def print_words(words):
    for word in words:
        print(word)

init_words = gen_words()
right_left_words = split_words(init_words)
left_words, right_words = tee_pieces(right_left_words)
print("Left halves:")
print_words(left_words)
print("Right halves:")
print_words(right_words)

這正確地分割了生成器,導致left_words包含左半部分,right_words包含右半部分。

當我嘗試使用上面注釋掉的行來參數化要創建的生成器的數量時出現問題。 據我所知它應該是等價的,但是當我使用那條線時,left_words和right_words最終都會包含該字的右半部分,給出如下輸出:

Left halves:
lo,
y
me
s
ed!
Right halves:
lo,
y
me
s
ed!

為什么會這樣? 我怎樣才能達到理想的結果,即參數化將發電機分成多少件?

這與python的詞法范圍規則有關。 展示它的經典“令人驚訝”的例子:

funcs = [ lambda: i for i in range(3) ]
print(funcs[0]())
=> 2  #??
print(funcs[1]())
=> 2  #??
print(funcs[2]())
=> 2

您的示例是相同規則的另一個結果。

要修復,您可以使用附加功能“中斷”范圍界定:

def make_gen(i):
    return (x[i] for x in copies[i])
dropped_copies = [make_gen(i) for i in range(2)]

make_gen i的值綁定到傳遞給make_gen的特定調用的特定值,從而實現所需的行為。 如果沒有它,它將綁定“名為i的變量的當前值”,它最終為您創建的所有生成器的相同值(因為只有一個名為i變量)。

要添加到shx2的答案,您還可以用lambda替換附加函數:

dropped_copies = [(lambda j: (x[j] for x in copies[j]))(i) for i in range(2)]

當lambda被調用時,這也會創建一個新的范圍,因為不同的變量名稱非常清楚。 然而,它也可以使用相同的名稱,因為lambda中的參數會影響生成器內的參數:

dropped_copies = [(lambda i: (x[i] for x in copies[i]))(i) for i in range(2)]

這種范圍似乎非常混亂,但如果將生成器重寫為for循環則變得更直觀:

dropped_copies = []
for i in range(2):
    dropped_copies.append((x[i] for x in copies[i]))

請注意,這與原始列表推導版本相同。

這是因為dropped_copies是一對迭代器,當迭代器被計算時, i已經增加到1。

嘗試使用list comprehension,你可以看到區別:

dropped_copies = [[x[i] for x in copies[i]] for i in range(2)]

暫無
暫無

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

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