整数のリストを入力として受け取るPythonスクリプトがあり、一度に4つの整数を処理する必要があります。残念ながら、入力を制御できません。または、4要素のタプルのリストとして渡されます。現在、私はそれを次のように繰り返しています:
for i in xrange(0, len(ints), 4):
# dummy op for example code
foo += ints[i] * ints[i + 1] + ints[i + 2] * ints[i + 3]
しかし、これは「C-think」によく似ているため、この状況に対処するためのPython的な方法があると思われます。リストは反復後に破棄されるため、保存する必要はありません。おそらくこのようなものの方が良いでしょうか?
while ints:
foo += ints[0] * ints[1] + ints[2] * ints[3]
ints[0:4] = []
それでも、まだまったく「感じる」わけではありません。 :-/
Pythonの recipes セクションから変更 itertools docs:
from itertools import Zip_longest
def grouper(iterable, n, fillvalue=None):
args = [iter(iterable)] * n
return Zip_longest(*args, fillvalue=fillvalue)
例
例を簡潔にするための擬似コード。
grouper('ABCDEFG', 3, 'x') --> 'ABC' 'DEF' 'Gxx'
注:Python 2では、izip_longest
の代わりにZip_longest
を使用します。
def chunker(seq, size):
return (seq[pos:pos + size] for pos in range(0, len(seq), size))
# (in python 2 use xrange() instead of range() to avoid allocating a list)
シンプル。簡単です。速い。任意のシーケンスで動作します:
text = "I am a very, very helpful text"
for group in chunker(text, 7):
print repr(group),
# 'I am a ' 'very, v' 'ery hel' 'pful te' 'xt'
print '|'.join(chunker(text, 10))
# I am a ver|y, very he|lpful text
animals = ['cat', 'dog', 'rabbit', 'duck', 'bird', 'cow', 'gnu', 'fish']
for group in chunker(animals, 3):
print group
# ['cat', 'dog', 'rabbit']
# ['duck', 'bird', 'cow']
# ['gnu', 'fish']
私はのファンです
chunk_size= 4
for i in range(0, len(ints), chunk_size):
chunk = ints[i:i+chunk_size]
# process chunk of size <= chunk_size
import itertools
def chunks(iterable,size):
it = iter(iterable)
chunk = Tuple(itertools.islice(it,size))
while chunk:
yield chunk
chunk = Tuple(itertools.islice(it,size))
# though this will throw ValueError if the length of ints
# isn't a multiple of four:
for x1,x2,x3,x4 in chunks(ints,4):
foo += x1 + x2 + x3 + x4
for chunk in chunks(ints,4):
foo += sum(chunk)
別の方法:
import itertools
def chunks2(iterable,size,filler=None):
it = itertools.chain(iterable,itertools.repeat(filler,size-1))
chunk = Tuple(itertools.islice(it,size))
while len(chunk) == size:
yield chunk
chunk = Tuple(itertools.islice(it,size))
# x2, x3 and x4 could get the value 0 if the length is not
# a multiple of 4.
for x1,x2,x3,x4 in chunks2(ints,4,0):
foo += x1 + x2 + x3 + x4
from itertools import izip_longest
def chunker(iterable, chunksize, filler):
return izip_longest(*[iter(iterable)]*chunksize, fillvalue=filler)
セットとジェネレーターでも機能するソリューションが必要でした。非常に短くてきれいなものは思いつきませんでしたが、少なくともかなり読みやすいです。
def chunker(seq, size):
res = []
for el in seq:
res.append(el)
if len(res) == size:
yield res
res = []
if res:
yield res
リスト:
>>> list(chunker([i for i in range(10)], 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
セット:
>>> list(chunker(set([i for i in range(10)]), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
ジェネレータ:
>>> list(chunker((i for i in range(10)), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
他の提案と似ていますが、まったく同じというわけではありませんが、シンプルで読みやすいので、このようにするのが好きです。
it = iter([1, 2, 3, 4, 5, 6, 7, 8, 9])
for chunk in Zip(it, it, it, it):
print chunk
>>> (1, 2, 3, 4)
>>> (5, 6, 7, 8)
この方法では、最後の部分的なチャンクは取得されません。最後のチャンクとして(9, None, None, None)
を取得する場合は、itertools
からizip_longest
を使用します。
この問題の理想的な解決策は、イテレータ(シーケンスだけでなく)で機能します。また、高速でなければなりません。
これはitertoolsのドキュメントで提供されるソリューションです。
def grouper(n, iterable, fillvalue=None):
#"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
return itertools.izip_longest(fillvalue=fillvalue, *args)
Macブックエアでipythonの%timeit
を使用すると、ループごとに47.5 usを取得します。
ただし、結果が同じサイズのグループになるようにパディングされるため、これは実際には機能しません。パディングなしのソリューションは、もう少し複雑です。最も素朴な解決策は次のとおりです。
def grouper(size, iterable):
i = iter(iterable)
while True:
out = []
try:
for _ in range(size):
out.append(i.next())
except StopIteration:
yield out
break
yield out
シンプルだがかなり遅い:ループあたり693 us
私が思いつく最良の解決策は、内部ループにislice
を使用します。
def grouper(size, iterable):
it = iter(iterable)
while True:
group = Tuple(itertools.islice(it, None, size))
if not group:
break
yield group
同じデータセットで、ループごとに305 usを取得します。
それよりも速く純粋なソリューションを取得できない場合、次のソリューションに重要な警告を提供します。入力データにfilldata
のインスタンスが含まれている場合、間違った答えを得る可能性があります。
def grouper(n, iterable, fillvalue=None):
#"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
for i in itertools.izip_longest(fillvalue=fillvalue, *args):
if Tuple(i)[-1] == fillvalue:
yield Tuple(v for v in i if v != fillvalue)
else:
yield i
私は本当にこの答えが好きではありませんが、かなり速いです。ループあたり124 us
まだ誰も言及していないので、ここにZip()
ソリューションがあります:
>>> def chunker(iterable, chunksize):
... return Zip(*[iter(iterable)]*chunksize)
シーケンスの長さが常にチャンクサイズで割り切れる場合、またはそうでない場合は後続のチャンクを気にしない場合にのみ機能します。
例:
>>> s = '1234567890'
>>> chunker(s, 3)
[('1', '2', '3'), ('4', '5', '6'), ('7', '8', '9')]
>>> chunker(s, 4)
[('1', '2', '3', '4'), ('5', '6', '7', '8')]
>>> chunker(s, 5)
[('1', '2', '3', '4', '5'), ('6', '7', '8', '9', '0')]
または、 itertools.izip を使用して、リストではなくイテレーターを返します。
>>> from itertools import izip
>>> def chunker(iterable, chunksize):
... return izip(*[iter(iterable)]*chunksize)
パディングは @ΤΖΩΤΖΙΟΥの答え を使用して修正できます。
>>> from itertools import chain, izip, repeat
>>> def chunker(iterable, chunksize, fillvalue=None):
... it = chain(iterable, repeat(fillvalue, chunksize-1))
... args = [it] * chunksize
... return izip(*args)
Zip()の代わりにmap()を使用すると、J.F。Sebastianの答えのパディングの問題が修正されます。
>>> def chunker(iterable, chunksize):
... return map(None,*[iter(iterable)]*chunksize)
例:
>>> s = '1234567890'
>>> chunker(s, 3)
[('1', '2', '3'), ('4', '5', '6'), ('7', '8', '9'), ('0', None, None)]
>>> chunker(s, 4)
[('1', '2', '3', '4'), ('5', '6', '7', '8'), ('9', '0', None, None)]
>>> chunker(s, 5)
[('1', '2', '3', '4', '5'), ('6', '7', '8', '9', '0')]
外部パッケージの使用を気にしない場合は、 iteration_utilities.grouper
from iteration_utilties
を使用できます。 1。 (シーケンスだけでなく)すべての反復可能オブジェクトをサポートします。
from iteration_utilities import grouper
seq = list(range(20))
for group in grouper(seq, 4):
print(group)
印刷するもの:
(0, 1, 2, 3)
(4, 5, 6, 7)
(8, 9, 10, 11)
(12, 13, 14, 15)
(16, 17, 18, 19)
長さがグループサイズの倍数ではない場合、最後のグループの埋め込み(不完全な最後のグループ)または切り捨て(不完全な最後のグループの破棄)もサポートします。
from iteration_utilities import grouper
seq = list(range(17))
for group in grouper(seq, 4):
print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)
# (16,)
for group in grouper(seq, 4, fillvalue=None):
print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)
# (16, None, None, None)
for group in grouper(seq, 4, truncate=True):
print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)
1 免責事項:私はそのパッケージの著者です。
小さな機能や物を使うことは本当に魅力的ではありません。私はスライスを使用することを好みます:
data = [...]
chunk_size = 10000 # or whatever
chunks = [data[i:i+chunk_size] for i in xrange(0,len(data),chunk_size)]
for chunk in chunks:
...
リストが大きい場合、これを行うための最もパフォーマンスの高い方法は、ジェネレーターを使用することです。
def get_chunk(iterable, chunk_size):
result = []
for item in iterable:
result.append(item)
if len(result) == chunk_size:
yield Tuple(result)
result = []
if len(result) > 0:
yield Tuple(result)
for x in get_chunk([1,2,3,4,5,6,7,8,9,10], 3):
print x
(1, 2, 3)
(4, 5, 6)
(7, 8, 9)
(10,)
NumPyを使用すると簡単です。
ints = array([1, 2, 3, 4, 5, 6, 7, 8])
for int1, int2 in ints.reshape(-1, 2):
print(int1, int2)
出力:
1 2
3 4
5 6
7 8
別のアプローチは、iter
の2つの引数形式を使用することです。
from itertools import islice
def group(it, size):
it = iter(it)
return iter(lambda: Tuple(islice(it, size)), ())
これはパディングを使用するように簡単に調整できます(これは Markus Jarderot の答えに似ています):
from itertools import islice, chain, repeat
def group_pad(it, size, pad=None):
it = chain(iter(it), repeat(pad))
return iter(lambda: Tuple(islice(it, size)), (pad,) * size)
これらはオプションのパディングのために組み合わせることができます:
_no_pad = object()
def group(it, size, pad=_no_pad):
if pad == _no_pad:
it = iter(it)
sentinel = ()
else:
it = chain(iter(it), repeat(pad))
sentinel = (pad,) * size
return iter(lambda: Tuple(islice(it, size)), sentinel)
私が何かを見逃さない限り、ジェネレータ式を使用した次の簡単な解決策は言及されていません。 チャンクのサイズと数の両方が既知である(これがよくあるケースです)、およびパディングが不要であると想定しています:
def chunks(it, n, m):
"""Make an iterator over m first chunks of size n.
"""
it = iter(it)
# Chunks are presented as tuples.
return (Tuple(next(it) for _ in range(n)) for _ in range(m))
ジェネレーターをサポートするインポートのないチャンカーは次のとおりです。
def chunks(seq, size):
it = iter(seq)
while True:
ret = Tuple(next(it) for _ in range(size))
if len(ret) == size:
yield ret
else:
raise StopIteration()
使用例:
>>> def foo():
... i = 0
... while True:
... i += 1
... yield i
...
>>> c = chunks(foo(), 3)
>>> c.next()
(1, 2, 3)
>>> c.next()
(4, 5, 6)
>>> list(chunks('abcdefg', 2))
[('a', 'b'), ('c', 'd'), ('e', 'f')]
def chunker(iterable, n):
"""Yield iterable in chunk sizes.
>>> chunks = chunker('ABCDEF', n=4)
>>> chunks.next()
['A', 'B', 'C', 'D']
>>> chunks.next()
['E', 'F']
"""
it = iter(iterable)
while True:
chunk = []
for i in range(n):
try:
chunk.append(next(it))
except StopIteration:
yield chunk
raise StopIteration
yield chunk
if __== '__main__':
import doctest
doctest.testmod()
J.F. Sebastian
による解決策について here :
def chunker(iterable, chunksize):
return Zip(*[iter(iterable)]*chunksize)
それは賢いですが、1つの欠点があります-常にタプルを返します。代わりに文字列を取得する方法は?
もちろん''.join(chunker(...))
を書くこともできますが、一時的なタプルはとにかく構築されます。
次のように、独自のZip
を記述することで、一時的なタプルを取り除くことができます。
class IteratorExhausted(Exception):
pass
def translate_StopIteration(iterable, to=IteratorExhausted):
for i in iterable:
yield i
raise to # StopIteration would get ignored because this is generator,
# but custom exception can leave the generator.
def custom_Zip(*iterables, reductor=Tuple):
iterators = Tuple(map(translate_StopIteration, iterables))
while True:
try:
yield reductor(next(i) for i in iterators)
except IteratorExhausted: # when any of iterators get exhausted.
break
それから
def chunker(data, size, reductor=Tuple):
return custom_Zip(*[iter(data)]*size, reductor=reductor)
使用例:
>>> for i in chunker('12345', 2):
... print(repr(i))
...
('1', '2')
('3', '4')
>>> for i in chunker('12345', 2, ''.join):
... print(repr(i))
...
'12'
'34'
私は自分のチャンクをパディングしたくないので、その要件は不可欠です。反復可能なものに取り組む能力も必要条件だと思います。そのため、受け入れられた答え https://stackoverflow.com/a/434411/1074659 を拡張することにしました。
パディングされた値を比較およびフィルタリングする必要があるため、パディングが不要な場合、このアプローチではパフォーマンスがわずかに低下します。ただし、大きなチャンクサイズの場合、このユーティリティは非常に高性能です。
#!/usr/bin/env python3
from itertools import Zip_longest
_UNDEFINED = object()
def chunker(iterable, chunksize, fillvalue=_UNDEFINED):
"""
Collect data into chunks and optionally pad it.
Performance worsens as `chunksize` approaches 1.
Inspired by:
https://docs.python.org/3/library/itertools.html#itertools-recipes
"""
args = [iter(iterable)] * chunksize
chunks = Zip_longest(*args, fillvalue=fillvalue)
yield from (
filter(lambda val: val is not _UNDEFINED, chunk)
if chunk[-1] is _UNDEFINED
else chunk
for chunk in chunks
) if fillvalue is _UNDEFINED else chunks
def group_by(iterable, size):
"""Group an iterable into lists that don't exceed the size given.
>>> group_by([1,2,3,4,5], 2)
[[1, 2], [3, 4], [5]]
"""
sublist = []
for index, item in enumerate(iterable):
if index > 0 and index % size == 0:
yield sublist
sublist = []
sublist.append(item)
if sublist:
yield sublist
2番目の方法では、次の方法で4つの次のグループに進みます。
ints = ints[4:]
ただし、パフォーマンスの測定は行っていないため、どちらが効率的かはわかりません。
そうは言っても、通常は最初の方法を選択します。それはきれいではありませんが、それは多くの場合、外の世界とのインターフェースの結果です。
私はこのアプローチが好きです。シンプルで魔法のようではなく、すべての反復可能なタイプをサポートし、インポートを必要としません。
def chunk_iter(iterable, chunk_size):
it = iter(iterable)
while True:
chunk = Tuple(next(it) for _ in range(chunk_size))
if not chunk:
break
yield chunk
さらに別の答え、その利点は次のとおりです。
1)わかりやすい
2)シーケンスだけでなく、反復可能なもので動作します(上記の回答の一部は、ファイルハンドルで停止します)
3)チャンクを一度にメモリにロードしません
4)メモリ内の同じイテレータへの参照のチャンク長リストを作成しません
5)リストの最後に塗りつぶし値のパディングなし
そうは言っても、私は時間を計っていないので、より賢い方法のいくつかよりも遅くなるかもしれませんし、ユースケースを考えると利点のいくつかは無関係かもしれません。
def chunkiter(iterable, size):
def inneriter(first, iterator, size):
yield first
for _ in xrange(size - 1):
yield iterator.next()
it = iter(iterable)
while True:
yield inneriter(it.next(), it, size)
In [2]: i = chunkiter('abcdefgh', 3)
In [3]: for ii in i:
for c in ii:
print c,
print ''
...:
a b c
d e f
g h
更新:
内部ループと外部ループが同じイテレーターから値をプルしているという事実によるいくつかの欠点:
1)continueは、外側のループで期待どおりに動作しません-チャンクをスキップするのではなく、次の項目に進むだけです。ただし、外側のループでテストするものがないため、これは問題のようには見えません。
2)内部ループでブレークが期待どおりに機能しない-反復子の次の項目で制御が再び内部ループで終了します。チャンク全体をスキップするには、内部反復子(上記のii)をTupleでラップします。 for c in Tuple(ii)
、またはフラグを設定してイテレータを使い果たします。
リストimport itertools
へのすべての変換を回避するには:
>>> for k, g in itertools.groupby(xrange(35), lambda x: x/10):
... list(g)
生産物:
...
0 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1 [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
2 [20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
3 [30, 31, 32, 33, 34]
>>>
groupby
をチェックしましたが、リストに変換したりlen
を使用したりしないので、実際に使用されるまで各値の解決を遅らせると思います。残念ながら、(現時点で)利用可能な回答のどれもこのバリエーションを提供していないようでした。
明らかに、各項目を順番に処理する必要がある場合、gのforループをネストします。
for k,g in itertools.groupby(xrange(35), lambda x: x/10):
for i in g:
# do what you need to do with individual items
# now do what you need to do with the whole group
これに対する私の特定の関心は、最大1000のバッチで変更をGmail APIに送信するためにジェネレーターを使用する必要があることです。
messages = a_generator_which_would_not_be_smart_as_a_list
for idx, batch in groupby(messages, lambda x: x/1000):
batch_request = BatchHttpRequest()
for message in batch:
batch_request.add(self.service.users().messages().modify(userId='me', id=message['id'], body=msg_labels))
http = httplib2.Http()
self.credentials.authorize(http)
batch_request.execute(http=http)
かなりPythonicです(split_groups
関数の本体をインライン化することもできます)
import itertools
def split_groups(iter_in, group_size):
return ((x for _, x in item) for _, item in itertools.groupby(enumerate(iter_in), key=lambda x: x[0] // group_size))
for x, y, z, w in split_groups(range(16), 4):
foo += x * y + z * w
サイズ4
のチャンクでリストx
を反復処理する1行のアドホックソリューション-
for a, b, c, d in Zip(x[0::4], x[1::4], x[2::4], x[3::4]):
... do something with a, b, c and d ...
最初に、16進数を含む文字列を解析するために、文字列を部分文字列に分割するように設計しました。
今日、私はそれを複雑ではあるがシンプルなジェネレーターに変えました。
def chunker(iterable, size, reductor, condition):
it = iter(iterable)
def chunk_generator():
return (next(it) for _ in range(size))
chunk = reductor(chunk_generator())
while condition(chunk):
yield chunk
chunk = reductor(chunk_generator())
iterable
は、入力データを含むイテレータ/イテレータ/ジェネレータを含む/生成/反復する、size
は、もちろん、取得したいチャンクのサイズです。reductor
は呼び出し可能オブジェクトであり、チャンクのコンテンツを反復処理するジェネレーターを受け取ります。
シーケンスまたは文字列を返すことを期待していますが、それを要求しません。
この引数として、たとえばlist
、Tuple
、set
、frozenset
、
またはその他の手の込んだもの。この関数を渡して、文字列を返します
(iterable
が文字列を含む/生成する/繰り返すことを条件とする):
def concatenate(iterable):
return ''.join(iterable)
reductor
は、例外を発生させてジェネレーターを閉じる場合があることに注意してください。
condition
は、reductor
が返したものをすべて受け取る呼び出し可能オブジェクトです。
(True
に評価するものを返すことにより)承認して譲り渡すことにしました。
またはそれを辞退し、ジェネレータの作業を終了します(他の何かを返すか、例外を発生させます)。
iterable
の要素数がsize
で割り切れない場合、it
が使い果たされると、reductor
は、size
よりも少ない要素を生成するジェネレーターを受け取ります。
これらの要素を呼び出しましょう最後の要素。
この引数として渡す2つの関数を招待しました。
lambda x:x
-lasts elementsが生成されます。
lambda x: len(x)==<size>
-lasts elementsは拒否されます。size
と等しい数を使用して<size>
を置き換えます
この答えは文字列のリストを分割する、f.ex. PEP8行の長さのコンプライアンスを達成するには:
def split(what, target_length=79):
'''splits list of strings into sublists, each
having string length at most 79'''
out = [[]]
while what:
if len("', '".join(out[-1])) + len(what[0]) < target_length:
out[-1].append(what.pop(0))
else:
if not out[-1]: # string longer than target_length
out[-1] = [what.pop(0)]
out.append([])
return out
使用
>>> split(['deferred_income', 'long_term_incentive', 'restricted_stock_deferred', 'shared_receipt_with_poi', 'loan_advances', 'from_messages', 'other', 'director_fees', 'bonus', 'total_stock_value', 'from_poi_to_this_person', 'from_this_person_to_poi', 'restricted_stock', 'salary', 'total_payments', 'exercised_stock_options'], 75)
[['deferred_income', 'long_term_incentive', 'restricted_stock_deferred'], ['shared_receipt_with_poi', 'loan_advances', 'from_messages', 'other'], ['director_fees', 'bonus', 'total_stock_value', 'from_poi_to_this_person'], ['from_this_person_to_poi', 'restricted_stock', 'salary', 'total_payments'], ['exercised_stock_options']]
これを行うためのきれいな方法はないようです。 ここ は、次のような多くのメソッドがあるページです。
def split_seq(seq, size):
newseq = []
splitsize = 1.0/size*len(seq)
for i in range(size):
newseq.append(seq[int(round(i*splitsize)):int(round((i+1)*splitsize))])
return newseq
イテレータをリストから外すことで、リストのスライスをコピーするだけではないことを望んでいます。ジェネレータはスライスでき、自動的にジェネレータとして機能しますが、リストは1000エントリの巨大なチャンクにスライスされるため、効率が低下します。
def iter_group(iterable, batch_size:int):
length = len(iterable)
start = batch_size*-1
end = 0
while(end < length):
start += batch_size
end += batch_size
if type(iterable) == list:
yield (iterable[i] for i in range(start,min(length-1,end)))
else:
yield iterable[start:end]
使用法:
items = list(range(1,1251))
for item_group in iter_group(items, 1000):
for item in item_group:
print(item)
リストが同じサイズの場合、それらをZip()
を使用して4タプルのリストに結合できます。例えば:
# Four lists of four elements each.
l1 = range(0, 4)
l2 = range(4, 8)
l3 = range(8, 12)
l4 = range(12, 16)
for i1, i2, i3, i4 in Zip(l1, l2, l3, l4):
...
Zip()
関数が生成するものは次のとおりです。
>>> print l1
[0, 1, 2, 3]
>>> print l2
[4, 5, 6, 7]
>>> print l3
[8, 9, 10, 11]
>>> print l4
[12, 13, 14, 15]
>>> print Zip(l1, l2, l3, l4)
[(0, 4, 8, 12), (1, 5, 9, 13), (2, 6, 10, 14), (3, 7, 11, 15)]
リストが大きく、リストを組み合わせて大きなリストにしたくない場合は、リストではなくイテレーターを生成するitertools.izip()
を使用します。
from itertools import izip
for i1, i2, i3, i4 in izip(l1, l2, l3, l4):
...
一時的なリストを作成せずに、反復可能の反復可能を取得するためにitertools.groupby
を機能させるのは簡単です:
groupby(iterable, (lambda x,y: (lambda z: x.next()/y))(count(),100))
ネストされたラムダによって先送りされないでください。外側のラムダは、count()
ジェネレーターと定数100
を内側のラムダのスコープに入れるために1回だけ実行されます。
これを使用して行のチャンクをmysqlに送信します。
for k,v in groupby(bigdata, (lambda x,y: (lambda z: x.next()/y))(count(),100))):
cursor.executemany(sql, v)
リスト内包表記を使用しない理由
l = [1 , 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
n = 4
filler = 0
fills = len(l) % n
chunks = ((l + [filler] * fills)[x * n:x * n + n] for x in range(int((len(l) + n - 1)/n)))
print(chunks)
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 0]]