Menambahkan hasil kumpulan ke dict

Saya memiliki fungsi yang menerima dua masukan yang disediakan oleh kombinasi itertools, dan menghasilkan solusi. Kedua input tersebut harus disimpan sebagai tupel yang membentuk kunci dalam dict, sedangkan hasilnya adalah nilainya.

Saya dapat menggabungkan ini dan mendapatkan semua hasilnya sebagai daftar, yang kemudian dapat saya masukkan ke dalam kamus satu per satu, tetapi ini tampaknya tidak efisien. Apakah ada cara untuk mendapatkan hasil saat setiap pekerjaan selesai, dan langsung menambahkannya ke dict?

Intinya, saya memiliki kode di bawah ini:

all_solutions = {}
for start, goal in itertools.combinations(graph, 2):
    all_solutions[(start, goal)] = search(graph, start, goal)

Saya mencoba memparalelkannya sebagai berikut:

all_solutions = {}
manager = multiprocessing.Manager()
graph_pool = manager.dict(graph)
pool = multiprocessing.Pool()
results = pool.starmap(search, zip(itertools.repeat(graph_pool),
                                   itertools.combinations(graph, 2)))
for i, start_goal in enumerate(itertools.combinations(graph, 2)):
    start, goal = start_goal[0], start_goal[1]
    all_solutions[(start, goal)] = results[i]

Yang benar-benar berfungsi, tetapi diulang dua kali, sekali di kumpulan, dan sekali untuk menulis ke dict (belum lagi pembongkaran Tupel yang kikuk).


person Teknophilia    schedule 14.02.2018    source sumber
comment
FYI, tidak perlu membongkar tupel yang kikuk. Anda hanya perlu lebih banyak tanda kurung: for i, (start, goal) in enumerate(itertools.combinations(graph, 2)): membongkar langsung ke start dan goal (tanda kurung bukan opsional dalam kasus ini, karena Anda memerlukannya untuk memperjelas bahwa sedang terjadi pembongkaran bertingkat).   -  person ShadowRanger    schedule 14.02.2018
comment
@ShadowRanger, ah senang mengetahuinya; terima kasih!   -  person Teknophilia    schedule 14.02.2018


Jawaban (1)


Hal ini dimungkinkan, Anda hanya perlu beralih menggunakan fungsi pemetaan malas (bukan map atau starmap, yang harus menyelesaikan penghitungan semua hasil sebelum Anda dapat mulai menggunakan salah satu dari fungsi tersebut):

from functools import partial
from itertools import tee

manager = multiprocessing.Manager()
graph_pool = manager.dict(graph)
pool = multiprocessing.Pool()

# Since you're processing in order and in parallel, tee might help a little
# by only generating the dict keys/search arguments once. That said, 
# combinations of n choose 2 are fairly cheap; the overhead of tee's caching
# might overwhelm the cost of just generating the combinations twice
startgoals1, startgoals2 = tee(itertools.combinations(graph, 2))

# Use partial binding of search with graph_pool to be able to use imap
# without a wrapper function; using imap lets us consume results as they become
# available, so the tee-ed generators don't store too many temporaries
results = pool.imap(partial(search, graph_pool), startgoals2))

# Efficiently create the dict from the start/goal pairs and the results of the search
# This line is eager, so it won't complete until all the results are generated, but
# it will be consuming the results as they become available in parallel with
# calculating the results
all_solutions = dict(zip(startgoals1, results))
person ShadowRanger    schedule 14.02.2018
comment
Wah, tepat sekali. parsial itulah yang saya cari, terima kasih! - person Teknophilia; 14.02.2018
comment
@Teknofilia: Yar. Peringatan: partial akan lambat jika argumen terikat mahal untuk pickle. Dalam hal ini, ini adalah dict terkelola yang ditujukan untuk multiprocessing, jadi biaya pengawetan hanyalah informasi yang diperlukan untuk menyambung kembali ke pengelola (cukup murah), tidak menyalin semua data, tetapi jika argumennya besar/mahal untuk pengawetan, partial bukan cara yang tepat. Jawaban saya atas pertanyaan itu menjelaskan mengapa fungsi wrapper global dengan argumen default bisa menjadi ide yang lebih baik dalam kasus seperti itu. - person ShadowRanger; 14.02.2018
comment
terimakasih atas peringatannya. Dict yang dikelola sendiri agak besar, tetapi jika saya memahaminya dengan benar, itu hanya disambungkan kembali - tidak diduplikasi - jadi seharusnya tidak menjadi masalah. Saya membaca ini dan jawaban Anda sangat membantu dalam pemahaman. - person Teknophilia; 14.02.2018