Создание подписок

Противоположность выравниванию списка.

Учитывая, что список и длина 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.