Создание списка отдельных элементов списка, умноженных n раз

Я довольно новичок в Python и думаю, что это должна быть довольно распространенная проблема, но не может найти решение. Я уже просмотрел эту страницу и нашел ее полезной для одного элемента, но я изо всех сил пытаюсь расширить пример до нескольких элементов, не используя цикл «для». Я запускаю этот бит кода для 250 ходоков через Emcee, поэтому я ищу самый быстрый способ.

У меня есть список чисел, a = [x,y,z] которые я хочу повторить b = [1,2,3] раза (например), поэтому я заканчиваю списком списков:

 [ [x], [y,y], [z,z,z] ] 

Для цикла «for» у меня есть:

 c = [ ] for i in range (0,len(a)): c.append([a[i]]*b[i]) 

Что делает именно то, что я хочу, но означает, что мой код мучительно медленный. Я также попытался наивно превращать a и b в массивы и делать [a]*b в надежде, что он будет умножать элемент за элементом, но без радости.

Вы можете использовать zip и список:

 >>> a = ['x','y','z'] >>> b = [1,2,3] >>> [[x]*y for x,y in zip(a,b)] [['x'], ['y', 'y'], ['z', 'z', 'z']] 

или:

 >>> [[x for _ in xrange(y)] for x,y in zip(a,b)] [['x'], ['y', 'y'], ['z', 'z', 'z']] 

zip сначала создаст весь список в памяти, чтобы использовать итератор itertools.izip

Если a содержит измененные объекты, такие как списки или списки списков, тогда вам, возможно, придется использовать copy.deepcopy здесь, потому что изменение одной копии также изменит другие копии:

 >>> from copy import deepcopy as dc >>> a = [[1 ,4],[2, 5],[3, 6, 9]] >>> f = [[dc(x) for _ in xrange(y)] for x,y in zip(a,b)] #now all objects are unique >>> [[id(z) for z in x] for x in f] [[172880236], [172880268, 172880364], [172880332, 172880492, 172880428]] 

сравнение времени (игнорирование импорта):

 >>> a = ['x','y','z']*10**4 >>> b = [100,200,300]*10**4 >>> %timeit [[x]*y for x,y in zip(a,b)] 1 loops, best of 3: 104 ms per loop >>> %timeit [[x]*y for x,y in izip(a,b)] 1 loops, best of 3: 98.8 ms per loop >>> %timeit map(lambda v: [v[0]]*v[1], zip(a,b)) 1 loops, best of 3: 114 ms per loop >>> %timeit map(list, map(repeat, a, b)) 1 loops, best of 3: 192 ms per loop >>> %timeit map(list, imap(repeat, a, b)) 1 loops, best of 3: 211 ms per loop >>> %timeit map(mul, [[x] for x in a], b) 1 loops, best of 3: 107 ms per loop >>> %timeit [[x for _ in xrange(y)] for x,y in zip(a,b)] 1 loops, best of 3: 645 ms per loop >>> %timeit [[x for _ in xrange(y)] for x,y in izip(a,b)] 1 loops, best of 3: 680 ms per loop 

Самый быстрый способ сделать это с помощью map () и operator.mul () :

 >>> from operator import mul >>> map(mul, [['x'], ['y'], ['z']], [1, 2, 3]) [['x'], ['y', 'y'], ['z', 'z', 'z']] 
 >>> from itertools import repeat >>> from itertools import starmap >>> a = ['x','y','z'] >>> b = [1,2,3] >>> starmap(repeat,zip(a,b)) 

starmap возвращает итерабельность, которая содержит значения, равные результату вызова repeat с аргументами, равными значениям, содержащимся в кортеже, в данном случае, например, ('x',1) .

 >>> for p in starmap(repeat,zip(a,b)): print(list(p)) ['x'] ['y', 'y'] ['z', 'z', 'z'] 

@kirelagin предложил версию без циклов, вот тот, который также не имеет lambda (помните, что решение от @AshwiniChaudhary наиболее читаемо)

 >>> from itertools import repeat >>> a = ['x','y','z'] >>> b = [1,2,3] >>> map(list, map(repeat, a, b)) [['x'], ['y', 'y'], ['z', 'z', 'z']] 

 >>> map(repeat, a, b) [repeat('x', 1), repeat('y', 2), repeat('z', 3)] 

создает список repeat объектов (используйте imap на Python 2.x, если вы хотите использовать ленивый итератор вместо списка), которые не занимают лишнего места в памяти, это здорово, если вы просто хотите перебирать элементы вместо их хранить)

Вот версия без циклов, если вам почему-то не нравятся:

 map(lambda v: [v[0]]*v[1], zip(a,b)) 

Я также должен предупредить вас, что эта версия немного медленнее, чем понимание списка:

 $ a = ['hi']*100 $ b = [20]*100 $ %timeit map(lambda v: [v[0]]*v[1], zip(a,b)) 10000 loops, best of 3: 101 us per loop %timeit [[x]*y for x,y in zip(a,b)] 10000 loops, best of 3: 74.1 us per loop 

Я бы также рекомендовал использовать itertools.izip вместо zip если вы на Python 2.