จะเข้าร่วมเครื่องกำเนิดสองเครื่องใน Python ได้อย่างไร


187

ฉันต้องการเปลี่ยนรหัสต่อไปนี้

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 ได้อย่างไร


1
ฉันต้องการให้ Python ทำงานด้วยวิธีนี้ มีข้อผิดพลาดเหมือนกันทุกประการ!
Adam Kurkiewicz

คำตอบ:


236

ฉันคิดว่าitertools.chain()ควรทำ


5
หนึ่งควรทราบว่าค่าส่งคืนของitertools.chain()ไม่ส่งคืนtypes.GeneratorTypeอินสแตนซ์ ในกรณีที่ประเภทที่แน่นอนมีความสำคัญ
ริกา

1
ทำไมคุณไม่ลองเขียนตัวอย่างผลงานออกมาด้วยล่ะ
Charlie Parker

75

ตัวอย่างของรหัส:

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

10
ทำไมไม่เพิ่มตัวอย่างนี้ในitertools.chain()คำตอบที่มีอยู่แล้วที่มีผู้โหวตสูงมาก
Jean-François Corbett

51

ใน Python (3.5 หรือสูงกว่า) คุณสามารถทำได้:

def concat(a, b):
    yield from a
    yield from b

7
ไพ ธ อนมาก
Ramazan Polat

9
ทั่วไปมากขึ้น: def chain(*iterables): for iterable in iterables: yield from iterable(ใส่defและforในบรรทัดที่แยกต่างหากเมื่อคุณเรียกใช้.)
wjandrea

ทุกสิ่งจากการให้ผลก่อนสิ่งใดจากถูกให้หรือถูกสลับกัน?
problemofficer

@problemofficer Yup aมีการตรวจสอบเท่านั้นจนกว่าทุกอย่างจะได้รับจากมันแม้ว่าbจะไม่ใช่ตัววนซ้ำก็ตาม TypeErrorสำหรับbไม่เป็น iterator จะเกิดขึ้นในภายหลัง
GeeTransit

36

ตัวอย่างง่ายๆ:

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

3
ทำไมไม่เพิ่มตัวอย่างนี้ในitertools.chain()คำตอบที่มีอยู่แล้วที่มีผู้โหวตสูงมาก
Jean-François Corbett

สิ่งนี้ไม่ถูกต้องเนื่องจากitertools.chainส่งคืนตัววนซ้ำไม่ใช่ตัวสร้าง
เดวิดเจ

ทำchain([1, 2, 3], [3, 4, 5])ไม่ได้เหรอ
คอร์แมน

10

ด้วย 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)

คุณกำลังใช้ความเข้าใจในรายการที่ไม่จำเป็น คุณกำลังใช้นิพจน์ตัวสร้างที่ไม่จำเป็นgennyเมื่อมันส่งคืนตัวสร้างแล้ว list(itertools.chain.from_iterable(genny(x)))กระชับยิ่งขึ้น
คอร์แมน

The! ist comprehension เป็นวิธีที่ง่ายในการสร้างเครื่องกำเนิดไฟฟ้าสองเครื่องตามคำถาม บางทีคำตอบของฉันอาจจะสับสนเล็กน้อยในเรื่องนั้น
andrew pate

ฉันเดาว่าเหตุผลที่ฉันเพิ่มคำตอบนี้ไปยังคนที่มีอยู่เดิมคือการช่วยเหลือผู้ที่มีกำเนิดจำนวนมากให้จัดการ
andrew pate

มันไม่ใช่วิธีที่ง่ายมีหลายวิธีที่ง่ายกว่า การใช้นิพจน์ตัวสร้างบนตัวสร้างที่มีอยู่จะลดประสิทธิภาพลงและตัวlistสร้างสามารถอ่านได้มากขึ้นจากนั้นความเข้าใจในรายการ วิธีการของคุณไม่สามารถอ่านได้มากขึ้นในเรื่องที่เกี่ยวกับ
Corman

คอร์แมนฉันเห็นด้วยว่าคอนสตรัคเตอร์ของคุณสามารถอ่านได้ง่ายกว่า มันจะเป็นการดีถ้าคุณได้เห็น 'หลาย ๆ วิธีที่ง่ายกว่า' แม้ว่า ... ฉันคิดว่าความคิดเห็นของ wjandrea ข้างต้นดูเหมือนจะทำเช่นเดียวกับ itertools.chain.from_iterable มันจะเป็นการดีที่จะแข่งกับพวกเขา
andrew pate

8

นี่คือการใช้นิพจน์ตัวสร้างกับfors ที่ซ้อนกัน:

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]

2
คำอธิบายเล็ก ๆ น้อย ๆ จะไม่เจ็บ
Ramazan Polat

ดีฉันไม่คิดว่าฉันสามารถอธิบายได้ดีกว่าเอกสารของ Python
Alexey

(เอกสารสำหรับนิพจน์เครื่องกำเนิดไฟฟ้าเชื่อมโยงจากคำตอบของฉันฉันไม่เห็นเหตุผลที่ดีในการคัดลอกและวางเอกสารลงในคำตอบของฉัน)
Alexey

3

ท่านสามารถใช้ตัวดำเนินการ unpack ได้*:

concat = (*gen1(), *gen2())

หมายเหตุ: ทำงานได้อย่างมีประสิทธิภาพมากที่สุดสำหรับ iterables 'non-lazy' สามารถใช้กับความเข้าใจที่แตกต่างกันได้ วิธีที่ต้องการสำหรับ concat ของเครื่องกำเนิดไฟฟ้าจะมาจากคำตอบจาก@Uduse


1

หากคุณต้องการให้เครื่องกำเนิดไฟฟ้าแยกจากกัน แต่ยังคงวนซ้ำในเวลาเดียวกันคุณสามารถใช้ 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

0

ให้บอกว่าเราต้องกำเนิด (gen1 และ gen 2) และเราต้องการทำการคำนวณพิเศษบางอย่างที่ต้องการผลลัพธ์ของทั้งคู่ เราสามารถส่งคืนผลลัพธ์ของฟังก์ชัน / การคำนวณดังกล่าวผ่านวิธีการแมปซึ่งจะส่งคืนตัวกำเนิดที่เราสามารถวนซ้ำ

ในสถานการณ์นี้จำเป็นต้องใช้ฟังก์ชัน / การคำนวณผ่านฟังก์ชัน lambda ส่วนที่ยากคือสิ่งที่เราตั้งใจทำในแผนที่และฟังก์ชั่นแลมบ์ดา

รูปแบบทั่วไปของโซลูชันที่เสนอ:

def function(gen1,gen2):
        for item in map(lambda x, y: do_somethin(x,y), gen1, gen2):
            yield item

0

โซลูชั่นที่ซับซ้อนทั้งหมดเหล่านั้น ...

แค่ทำ:

for dir in director_1, directory_2:
    for directory, dirs, files in os.walk(dir):
        do_something()

หากคุณต้องการที่จะ "เข้าร่วม" ทั้งเครื่องปั่นไฟจริงๆให้ทำ:

for directory, dirs, files in 
        [x for osw in [os.walk(director_1), os.walk(director_2)] 
               for x in osw]:
    do_something()

0

ฉันจะบอกว่าตามที่แนะนำในความคิดเห็นโดยผู้ใช้ "wjandrea" ทางออกที่ดีที่สุดคือ

def concat_generators(*args):
    for gen in args:
        yield from gen

ไม่เปลี่ยนประเภทที่ส่งคืนและเป็น pythonic จริงๆ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.