[英]How to join two generators in Python?
我想更改以下代碼
for directory, dirs, files in os.walk(directory_1):
do_something()
for directory, dirs, files in os.walk(directory_2):
do_something()
到這段代碼:
for directory, dirs, files in os.walk(directory_1) + os.walk(directory_2):
do_something()
我得到錯誤:
+ 不支持的操作數類型:“生成器”和“生成器”
如何在 Python 中加入兩個生成器?
itertools.chain()
應該這樣做。 它從一個一個一個一個地獲取多個迭代和產量,大致相當於:
def chain(*iterables):
for it in iterables:
for element in it:
yield element
使用示例:
from itertools import chain
g = (c for c in 'ABC') # Dummy generator, just for example
c = chain(g, 'DEF') # Chain the generator and a string
for item in c:
print(item)
輸出:
A
B
C
D
E
F
代碼示例:
from itertools import chain
def generator1():
for item in 'abcdef':
yield item
def generator2():
for item in '123456':
yield item
generator3 = chain(generator1(), generator2())
for item in generator3:
print item
在 Python(3.5 或更高版本)中,您可以執行以下操作:
def concat(a, b):
yield from a
yield from b
from itertools import chain
x = iter([1,2,3]) #Create Generator Object (listiterator)
y = iter([3,4,5]) #another one
result = chain(x, y) #Chained x and y
使用 itertools.chain.from_iterable 您可以執行以下操作:
def genny(start):
for x in range(start, start+3):
yield x
y = [1, 2]
ab = [o for o in itertools.chain.from_iterable(genny(x) for x in y)]
print(ab)
這里它使用了一個帶有嵌套for
s 的生成器表達式:
a = range(3)
b = range(5)
ab = (i for it in (a, b) for i in it)
assert list(ab) == [0, 1, 2, 0, 1, 2, 3, 4]
也可以使用解包運算符*
:
concat = (*gen1(), *gen2())
注意:對於“非惰性”迭代最有效。 也可以與不同類型的理解一起使用。 生成器 concat 的首選方法來自@Uduse的答案
2020 年更新:在 Python 3 和 Python 2 中工作
import itertools
iterA = range(10,15)
iterB = range(15,20)
iterC = range(20,25)
第一個選項
for i in itertools.chain(iterA, iterB, iterC):
print(i)
# 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
替代選項,在 python 2.6 中引入
for i in itertools.chain.from_iterable( [iterA, iterB, iterC] ):
print(i)
# 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
itertools.chain()是基本的。
如果您有可迭代的可迭代對象, itertools.chain.from_iterable()會很方便。 例如每個子目錄的文件列表,例如[ ["src/server.py", "src/readme.txt"], ["test/test.py"] ]
。
如果你想保持生成器分開但仍然同時迭代它們,你可以使用 zip():
注意:迭代在兩個生成器中較短的那個處停止
例如:
for (root1, dir1, files1), (root2, dir2, files2) in zip(os.walk(path1), os.walk(path2)):
for file in files1:
#do something with first list of files
for file in files2:
#do something with second list of files
(免責聲明:僅限 Python 3!)
與您想要的語法相似的東西是使用 splat 運算符來擴展兩個生成器:
for directory, dirs, files in (*os.walk(directory_1), *os.walk(directory_2)):
do_something()
這有效地將兩個生成器的單級展平為 3 元組的 N 元組(來自os.walk
),如下所示:
((directory1, dirs1, files1), (directory2, dirs2, files2), ...)
然后你的 for 循環遍歷這個 N 元組。
當然,通過簡單地將外圓括號替換為方括號,您可以獲得 3 元組的列表,而不是 3 元組的 N 元組:
for directory, dirs, files in [*os.walk(directory_1), *os.walk(directory_2)]:
do_something()
這會產生類似的東西:
[(directory1, dirs1, files1), (directory2, dirs2, files2), ...]
這種方法的好處是您不必導入任何東西,而且代碼也不多。
缺點是您將兩個生成器轉儲到一個集合中,然后迭代該集合,有效地執行兩次傳遞並可能使用大量內存。
假設我們必須生成生成器(gen1 和 gen 2),並且我們想要執行一些需要兩者結果的額外計算。 我們可以通過 map 方法返回此類函數/計算的結果,該方法又返回一個我們可以循環的生成器。
在這種情況下,需要通過 lambda 函數來實現函數/計算。 棘手的部分是我們的目標是在地圖及其 lambda 函數中做些什么。
建議解決方案的一般形式:
def function(gen1,gen2):
for item in map(lambda x, y: do_somethin(x,y), gen1, gen2):
yield item
我想說,正如用戶“wjandrea”在評論中所建議的那樣,最好的解決方案是
def concat_generators(*gens):
for gen in gens:
yield from gen
它不會改變返回的類型,並且是真正的 Pythonic。
您可以將任何生成器放入列表中。 雖然你不能組合生成器,但你可以組合列表。 這樣做的缺點是您實際上在 memory 中創建了 3 個列表,但優點是這非常易讀,不需要導入,並且是單行習語。
OP的解決方案。
for directory, dirs, files in list(os.walk(directory_1)) + list(os.walk(directory_2)):
do_something()
a = range(20)
b = range(10,99,3)
for v in list(a) + list(b):
print(v)
如果您想從 know 目錄之前和之后獲取文件路徑列表,您可以這樣做:
for r,d,f in os.walk(current_dir):
for dir in d:
if dir =='after':
after_dir = os.path.abspath(os.path.join(current_dir, dir))
for r,d,f in os.walk(after_dir):
after_flist.append([os.path.join(r,file)for file in f if file.endswith('json')])
elif dir =='before':
before_dir = os.path.abspath(os.path.join(current_dir, dir))
for r,d,f in os.walk(before_dir):
before_flist.append([os.path.join(r,file)for file in f if file.endswith('json')])
我知道有更好的答案,這是我覺得的簡單代碼。
如果您只需要執行一次並且不想再導入一個模塊,那么有一個簡單的解決方案......
做就是了:
for dir in directory_1, directory_2:
for directory, dirs, files in os.walk(dir):
do_something()
如果您真的想“加入”兩個生成器,請執行以下操作:
for directory, dirs, files in (
x for osw in [os.walk(directory_1), os.walk(directory_2)]
for x in osw
):
do_something()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.