concurrent.futures.Executor.map
は、指定された関数の呼び出し元となる可変数のイテラブルを取ります。 通常は展開されているタプルを生成するジェネレーターがある場合、どのように呼び出す必要がありますか?
生成されたタプルのそれぞれがマップする異なる引数として指定されているため、以下は機能しません。
args = ((a, b) for (a, b) in c)
for result in executor.map(f, *args):
pass
ジェネレーターがない場合、mapに必要な引数は次のようになります。
executor.map(
f,
(i[0] for i in args),
(i[1] for i in args),
...,
(i[N] for i in args),
)
map
呼び出しで_*
_を削除する必要があります:
_args = ((a, b) for b in c)
for result in executor.map(f, args):
pass
_
これはf
、len(args)
を呼び出します。ここで、f
は1つのパラメーターを受け入れる必要があります。
f
が2つのパラメーターを受け入れるようにするには、次のようなラムダ呼び出しを使用できます。
_args = ((a, b) for b in c)
for result in executor.map(lambda p: f(*p), args): # (*p) does the unpacking part
pass
_
c
の1つの引数from itertools import repeat
for result in executor.map(f, repeat(a), c):
pass
c
のアイテムを解凍する必要があり、c
を解凍できますfrom itertools import izip
for result in executor.map(f, *izip(*c)):
pass
c
のアイテムを解凍する必要がありますが、c
を解凍できませんf
を変更して単一の引数を取り、関数の引数をアンパックします。c
の各アイテムに可変数のメンバーがある場合、またはf
を数回だけ呼び出している場合:
executor.map(lambda args, f=f: f(*args), c)
c
から各項目をアンパックし、f
を呼び出す新しい関数を定義します。 f
でlambda
のデフォルト引数を使用すると、f
がlambda
内でローカルになり、ルックアップ時間が短縮されます。
固定数の引数があり、f
を何度も呼び出す必要がある場合:
from collections import deque
def itemtee(iterable, n=2):
def gen(it = iter(iterable), items = deque(), next = next):
popleft = items.popleft
extend = items.extend
while True:
if not items:
extend(next(it))
yield popleft()
return [gen()] * n
executor.map(f, *itemtee(c, n))
ここで、n
はf
への引数の数です。これは itertools.tee
。
Pythonで partial メソッドを使用して、カリー化を使用して新しい関数を作成できます
from concurrent.futures import ThreadPoolExecutor
from functools import partial
def some_func(param1, param2):
# some code
# currying some_func with 'a' argument is repeated
func = partial(some_func, a)
with ThreadPoolExecutor() as executor:
executor.map(func, list_of_args):
...
同じパラメータを複数渡す必要がある場合は、それらを partial メソッドに渡すことができます
func = partial(some_func, a, b, c)
したがって、takes argumentsの関数があり、3つの引数すべてがdynamicであり、呼び出しのたびに変化し続けると仮定します。例えば:
def multiply(a,b,c):
print(a * b * c)
スレッドを使用してこれを複数回呼び出すには、最初にタプルのリストを作成します。ここで、各タプルはa、b、cのバージョンです。
arguments = [(1,2,3), (4,5,6), (7,8,9), ....]
私たちはそれを知っていますconcurrent.futures
のmap
関数は、実行される関数のバージョンごとに、最初の引数をターゲット関数として受け入れ、2番目の引数を引数のリストとして受け入れます。したがって、次のような呼び出しを行う可能性があります。
for _ in executor.map(multiply, arguments) # Error
しかし、これはあなたにerror関数が期待する3 arguments but got only 1
。この問題を解決するために、ヘルパー関数を作成します。
def helper(numbers):
multiply(numbers[0], numbers[1], numbers[2])
これで、executorを使用してこの関数を次のように呼び出すことができます。
with ThreadPoolExecutor() as executor:
for _ in executor.map(helper, arguments):
pass
これで望ましい結果が得られるはずです。
ProcessPoolExecutor.map()
の場合:
Map(func、* iterables)に似ていますが、次の点が異なります。
反復可能オブジェクトは、遅延ではなく即座に収集されます。
funcは非同期に実行され、funcへの複数の呼び出しが同時に行われる場合があります。
python 3の下で次のスニペットを実行してみてください。そうすれば、非常に明確になります。
from concurrent.futures import ProcessPoolExecutor
def f(a, b):
print(a+b)
with ProcessPoolExecutor() as pool:
pool.map(f, (0, 1, 2, 3, 4, 5, 6, 7, 8, 9), (0, 1, 2))
# 0, 2, 4
array = [(i, i) for i in range(3)]
with ProcessPoolExecutor() as pool:
pool.map(f, *Zip(*array))
# 0, 2, 4
ここでは非常に多くの答えを見てきましたが、ラムダ式を使用するほど簡単なものはありません。
foo(x、y):渡す
上記のメソッドを同じ値、つまりxValとyValで10回呼び出したいですか? executorとしてconcurrent.futures.ThreadPoolExecutor()を使用:
for _ in executor.map( lambda _: foo(xVal, yVal), range(0, 10)):
pass