Làm thế nào để tham gia hai trình tạo trong Python?


187

Tôi muốn thay đổi mã sau đây

for directory, dirs, files in os.walk(directory_1):
    do_something()

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

mã này:

for directory, dirs, files in os.walk(directory_1) + os.walk(directory_2):
    do_something()

Tôi nhận được lỗi:

(các) loại toán hạng không được hỗ trợ cho +: 'trình tạo' và 'trình tạo'

Làm thế nào để tham gia hai trình tạo trong Python?


1
Tôi cũng muốn Python hoạt động theo cách này. Có chính xác lỗi tương tự!
Adam Kurkiewicz

Câu trả lời:


232

Tôi nghĩ itertools.chain()nên làm điều đó.


5
Bạn nên nhớ rằng giá trị trả về của itertools.chain()không trả về một types.GeneratorTypethể hiện. Chỉ trong trường hợp loại chính xác là rất quan trọng.
Riga

1
Tại sao bạn không viết ra một ví dụ làm việc?
Charlie Parker

74

Một ví dụ về mã:

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
Tại sao không thêm ví dụ này vào itertools.chain()câu trả lời đã có sẵn, được đánh giá cao ?
Jean-François Corbett

51

Trong Python (3,5 trở lên) bạn có thể làm:

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

7
Rất nhiều pythonic.
Ramazan Polat

9
Tổng quát hơn: def chain(*iterables): for iterable in iterables: yield from iterable(Đặt deffortrên các dòng riêng biệt khi bạn chạy nó.)
wjandrea

Là tất cả mọi thứ từ một sản lượng trước khi bất cứ điều gì từ b được mang lại hoặc chúng được xen kẽ?
vấn đề

@pro Hiệuofficer Yup. Chỉ ađược kiểm tra cho đến khi mọi thứ được mang lại từ nó, ngay cả khi bkhông phải là trình vòng lặp. Các TypeErrorcho bkhông phải là một iterator sẽ đi lên sau đó.
GeeTransit

35

Ví dụ đơn giản:

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
Tại sao không thêm ví dụ này vào itertools.chain()câu trả lời đã có sẵn, được đánh giá cao ?
Jean-François Corbett

Điều này không hoàn toàn đúng, vì itertools.chaintrả về một trình vòng lặp, không phải là trình tạo.
David J.

Bạn không thể làm gì chain([1, 2, 3], [3, 4, 5])?
Corman

10

Với itertools.chain.from_iterable, bạn có thể làm những việc như:

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)

Bạn đang sử dụng một sự hiểu biết danh sách không cần thiết. Bạn cũng đang sử dụng một biểu thức trình tạo không cần thiết gennykhi nó đã trả về một trình tạo. list(itertools.chain.from_iterable(genny(x)))ngắn gọn hơn nhiều
Corman

Hiểu! Ist là một cách dễ dàng để tạo ra hai máy phát điện, theo câu hỏi. Có lẽ câu trả lời của tôi là một chút phức tạp trong khía cạnh đó.
pate

Tôi đoán lý do tôi đã thêm câu trả lời này cho những câu hỏi hiện có là để giúp những người tình cờ có nhiều máy phát điện để giải quyết.
pate

Đó không phải là một cách dễ dàng, có nhiều cách dễ dàng hơn. Sử dụng các biểu thức của trình tạo trên một trình tạo hiện tại sẽ làm giảm hiệu suất và hàm listtạo dễ đọc hơn nhiều so với việc hiểu danh sách. Phương pháp của bạn là không thể đọc được nhiều hơn trong những liên quan.
Corman

Corman, tôi đồng ý rằng nhà xây dựng danh sách của bạn thực sự dễ đọc hơn. Thật tốt khi thấy 'nhiều cách dễ dàng hơn' của bạn mặc dù ... Tôi nghĩ rằng nhận xét của wjandrea ở trên có vẻ giống như itertools.chain.from_iterable, sẽ rất tốt nếu đua chúng và xem ai nhanh nhất.
pate

8

Ở đây nó đang sử dụng một biểu thức trình tạo với fors lồng nhau :

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
Một lời giải thích nhỏ sẽ không đau.
Ramazan Polat

Chà, tôi không nghĩ rằng tôi có thể giải thích điều này tốt hơn tài liệu của Python.
Alexey

(Tài liệu về biểu thức trình tạo được liên kết từ câu trả lời của tôi. Tôi không thấy lý do chính đáng để sao chép và dán tài liệu vào câu trả lời của mình.)
Alexey

3

Người ta cũng có thể sử dụng toán tử giải nén *:

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

LƯU Ý: Hoạt động hiệu quả nhất cho các lần lặp 'không lười biếng'. Cũng có thể được sử dụng với các loại hiểu khác nhau. Cách ưa thích cho concat máy phát điện sẽ là từ câu trả lời từ @Uduse


1

Nếu bạn muốn giữ các bộ tạo riêng biệt nhưng vẫn lặp lại trên chúng cùng một lúc, bạn có thể sử dụng zip ():

LƯU Ý: Lặp lại dừng ở ngắn hơn của hai máy phát

Ví dụ:

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

Hãy nói rằng chúng ta phải tạo máy phát điện (gen1 và gen 2) và chúng tôi muốn thực hiện một số tính toán bổ sung đòi hỏi kết quả của cả hai. Chúng ta có thể trả về kết quả của hàm / phép tính như vậy thông qua phương thức bản đồ, lần lượt trả về một trình tạo mà chúng ta có thể lặp lại.

Trong kịch bản này, hàm / tính toán cần được thực hiện thông qua hàm lambda. Phần khó khăn là những gì chúng tôi nhắm đến để làm bên trong bản đồ và chức năng lambda của nó.

Hình thức chung của giải pháp đề xuất:

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

0

Tất cả những giải pháp phức tạp đó ...

cứ làm đi:

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

Nếu bạn thực sự muốn "tham gia" cả hai máy phát điện, thì hãy làm:

for directory, dirs, files in 
        [x for osw in [os.walk(director_1), os.walk(director_2)] 
               for x in osw]:
    do_something()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.