more_itertools chunkedの処理を読む
実際のコードは以下
def chunked(iterable, n): """Break *iterable* into lists of length *n*: >>> list(chunked([1, 2, 3, 4, 5, 6], 3)) [[1, 2, 3], [4, 5, 6]] If the length of *iterable* is not evenly divisible by *n*, the last returned list will be shorter: >>> list(chunked([1, 2, 3, 4, 5, 6, 7, 8], 3)) [[1, 2, 3], [4, 5, 6], [7, 8]] To use a fill-in value instead, see the :func:`grouper` recipe. :func:`chunked` is useful for splitting up a computation on a large number of keys into batches, to be pickled and sent off to worker processes. One example is operations on rows in MySQL, which does not implement server-side cursors properly and would otherwise load the entire dataset into RAM on the client. """ return iter(partial(take, n, iter(iterable)), [])
要素を分解する
実際の処理はここだけ
return iter(partial(take, n, iter(iterable)), [])
iter, partial, takeという処理を組み合わせて実現してる、それぞれみてみる。
iter
https://docs.python.org/3/library/functions.html#iter
イテレータオブジェクトを返してくれる 引数の内容によって振る舞いがかわってくる
>>> iter(1) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'int' object is not iterable >>> iter("こんにちは") <str_iterator object at 0x10dbf7c18> >>> iter("こんにちは", "") Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: iter(v, w): v must be callable >>> iter(int, 0) <callable_iterator object at 0x10dbf7cc0>
1つだけ引数を渡す場合、イテレートできないようなオブジェクトだとTypeErrorになる
2つ引数を渡す場合、callできないようなオブジェクトだとTypeErrorになる、callした結果が2つ目の引数と一致するまでcallし続ける
partial
https://docs.python.org/3/library/functools.html#functools.partial
任意のfunctionにおいて、特定引数のdefault値が設定された新たなfunctionを生成してくれる
次みたいなのができるになる
>>> from functools import partial >>> def say(word): ... print(word) ... >>> say("こんにちは") こんにちは >>> new_say = partial(say, word="こんばんは") >>> new_say() こんばんは >>> new_say(word="おやすみ") おやすみ
take
イテレータオブジェクトの頭からn回処理した結果をリストとして返してくれる
def take(n, iterable): """Return first *n* items of the iterable as a list. >>> take(3, range(10)) [0, 1, 2] >>> take(5, range(3)) [0, 1, 2] Effectively a short replacement for ``next`` based iterator consumption when you want more than one item, but less than the whole iterator. """ return list(islice(iterable, n))
ここでつかってるisliceはイテレータオブジェクトの頭からn回処理した結果を返す
islice: https://docs.python.org/3.6/library/itertools.html#itertools.islice
list()しない場合とでみるとわかりやすいかも
>>> from itertools import islice >>> word = "こんにちは" >>> islice(word, 3) <itertools.islice object at 0x10dbf8728> >>> list(islice(word, 3)) ['こ', 'ん', 'に'] >>>
どんなことやらせてるのか
実際の処理の部分を再掲
def chunked(iterable, n): return iter(partial(take, n, iter(iterable)), [])
iter(iterable)
でイテレータオブジェクトを生成してるpartial(take, n, iter(iterable))
でtakeに対してnとiter(iterable)が与えられた状態のtake functionを生成してるiter(partial(take, n, iter(iterable)), [])
で新たに生成したtake functionの返りが[]
となるまで繰り返すようなイテレータオブジェクトを生成している
という感じのことだということがわかった。