Создание подписок
Противоположность выравниванию списка.
Учитывая, что список и длина n возвращают список подписок длины n.
def sublist(lst, n):
sub=[] ; result=[]
for i in lst:
sub+=[i]
if len(sub)==n: result+=[sub] ; sub=[]
if sub: result+=[sub]
return result
Пример:
Если список:
[1,2,3,4,5,6,7,8]
И n:
3
Возврат:
[[1, 2, 3], [4, 5, 6], [7, 8]]
Есть ли более красноречивый/лаконичный способ?
В стороне, что предпочтительнее при добавлении списков в списки (в контексте выше):
list1+=[list2]
Или:
list1.append(list2)
Учитывая, что (согласно Программе Summerfeild "Программирование на Python 3" ), они одинаковы?
Спасибо.
Ответы
Ответ 1
Такой список списков может быть построен с использованием понимания списка:
In [17]: seq=[1,2,3,4,5,6,7,8]
In [18]: [seq[i:i+3] for i in range(0,len(seq),3)]
Out[18]: [[1, 2, 3], [4, 5, 6], [7, 8]]
Существует также идентификатор группы::
In [19]: import itertools
In [20]: list(itertools.izip_longest(*[iter(seq)]*3))
Out[20]: [(1, 2, 3), (4, 5, 6), (7, 8, None)]
но обратите внимание, что отсутствующие элементы заполняются значением None. izip_longest может принимать параметр fillvalue
, если требуется нечто иное, кроме None.
list1+=[list2]
- отмечая скобки на этот раз - эквивалентно list1.append(list2)
. Мой самый высокий приоритет при написании кода является читабельностью,
не скорость. По этой причине я бы пошел с list1.append(list2)
. Однако читаемость субъективна и, вероятно, сильно зависит от того, с какими идиомами вы знакомы.
К счастью, в этом случае читаемость и скорость, похоже, совпадают:
In [41]: %timeit list1=[1,2,3]; list1.append(list2)
1000000 loops, best of 3: 612 ns per loop
In [42]: %timeit list1=[1,2,3]; list1+=[list2]
1000000 loops, best of 3: 847 ns per loop
Ответ 2
Как насчет следующего (где x
- ваш список):
[x[i:i+3] for i in range(0, len(x), 3)]
Это тривиально для обобщения для n!=3
.
Что касается вашего второго вопроса, они эквивалентны, поэтому я думаю, что это вопрос стиля. Однако убедитесь, что вы не запутываете append
с помощью extend
.
Ответ 3
Эта функция может принимать любые итерационные (не только последовательности известной длины):
import itertools
def grouper(n, it):
"grouper(3, 'ABCDEFG') --> ABC DEF G"
it = iter(it)
return iter(lambda: list(itertools.islice(it, n)), [])
print(list(grouper(3, [1,2,3,4,5,6,7,8,9,10])))
# [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
Ответ 4
Слышали ли вы о boltons
?
boltons
представляет собой набор утилит pure-Python в том же духе, что и, тем не менее, явно отсутствует - стандартная библиотека
Он имеет то, что вы хотите, встроенный, называемый chunked
from boltons import iterutils
iterutils.chunked([1,2,3,4,5,6,7,8], 3)
Вывод:
[[1, 2, 3], [4, 5, 6], [7, 8]]
И более привлекательным в boltons
является то, что он имеет chunked
как итератор, называемый chunked_iter
, поэтому вам не нужно хранить все это в памяти. Аккуратно, правильно?
Ответ 5
Я думаю, что эта функция разделения делает то, что вы ищете (хотя она работает с любым итератором, а не только с списками):
from itertools import islice
def take(n, it):
"Return first n items of the iterable as a list"
return list(islice(it, n))
def split(it, size):
it = iter(it)
size = int(size)
ret = take(size, it)
while ret:
yield ret
ret = take(size, it)
Edit: Что касается вашей справки, я всегда использую list.append(blah), поскольку мне кажется более идиоматичным, но я считаю, что они функционально эквивалентны.
Ответ 6
Я знаю, это выглядит как brainfuck, но работает:
>>> a = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
>>> n = 3
>>> [i for j in [[a[t:t+n] for x in a[:1:t+1] if (t%n)==False] for t in range(len(a))] for i in j]
[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12], [13, 14, 15]]
>>> n = 4
>>> [i for j in [[a[t:t+n] for x in a[:1:t+1] if (t%n)==False] for t in range(len(a))] for i in j]
[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15]]
Ответ 7
Для некоторых конкретных случаев может оказаться полезным использовать пакет numpy. В этом пакете есть reshape:
import numpy as np
x = np.array([1,2,3,4,5,6])
np.reshape(x, (-1,3))
Однако это решение не будет заполнять ваш список, если он не умножен на n.