Создать список одного элемента, повторяется n раз в Python - PullRequest
434 голосов
/ 11 августа 2010

Я знаю, что понимание списка будет делать это, но мне было интересно, есть ли еще более короткий (и более Pythonic?) Подход.

Я хочу создать серию списков, все различной длины.Каждый список будет содержать один и тот же элемент e, повторенный n раз (где n = длина списка).Как мне создать списки, не делая

[e for number in xrange(n)]

для каждого списка?

Ответы [ 6 ]

649 голосов
/ 11 августа 2010

Вы также можете написать:

[e] * n

Вы должны заметить, что если e является, например, пустым списком, вы получите список с n ссылками на один и тот же список, а не n независимых пустых списков.

Тестирование производительности

На первый взгляд кажется , что повторение - самый быстрый способ создать список из n идентичных элементов:

>>> timeit.timeit('itertools.repeat(0, 10)', 'import itertools', number = 1000000)
0.37095273281943264
>>> timeit.timeit('[0] * 10', 'import itertools', number = 1000000)
0.5577236771712819

Но подождите - это не честный тест ...

>>> itertools.repeat(0, 10)
repeat(0, 10)  # Not a list!!!

Функция itertools.repeat на самом деле не создает список, она просто создает объект, который можно использовать для создания списка, если хотите! Давайте попробуем это снова, но преобразуем в список:

>>> timeit.timeit('list(itertools.repeat(0, 10))', 'import itertools', number = 1000000)
1.7508119747063233

Так что если вы хотите список, используйте [e] * n. Если вы хотите генерировать элементы лениво, используйте repeat.

124 голосов
/ 11 августа 2010
>>> [5] * 4
[5, 5, 5, 5]

Будьте осторожны, когда повторяющийся элемент является списком. Список не будет клонирован: все элементы будут ссылаться на один и тот же список!

>>> x=[5]
>>> y=[x] * 4
>>> y
[[5], [5], [5], [5]]
>>> y[0][0] = 6
>>> y
[[6], [6], [6], [6]]
56 голосов
/ 03 июля 2014

Создание списка отдельных элементов, повторяющихся n раз в Python

Неизменяемые элементы

Для неизменяемых элементов, таких как None, строки, кортежи или frozensets, вы можете сделатьэто выглядит так:

[e] * 4

Обратите внимание, что это лучше всего использовать только с неизменяемыми элементами (строками, кортежами, frozensets,) в списке, поскольку все они указывают на один и тот же элемент в одном и том же месте в памяти.Я часто использую это, когда мне нужно построить таблицу со схемой из всех строк, чтобы мне не приходилось отображать один в один.

schema = ['string'] * len(columns)

Изменяемые элементы

Я уже давно использую Python, и никогда не видел ни одного варианта использования, где бы я делал это с изменяемым экземпляром.Вместо этого, чтобы получить, скажем, изменяемый пустой список, набор или диктовку, вы должны сделать что-то вроде этого:

list_of_lists = [[] for _ in columns]

Подчеркивание - это просто одноразовое имя переменной в этом контексте.

Если у вас есть только число, это будет:

list_of_lists = [[] for _ in range(4)]

_ не является особенным, но ваша программа проверки стиля среды кодирования, вероятно, пожалуется, если вы не собираетесь использовать переменную ииспользуйте любое другое имя.


Предостережения для использования неизменяемого метода с изменяемыми элементами:

Остерегайтесь делать это с изменяемыми объектами , когда вы меняете один из них,все они меняются, потому что они все один и тот же объект:

foo = [[]] *4
foo[0].append('x')

foo теперь возвращает:

[['x'], ['x'], ['x'], ['x']]

Но с неизменяемыми объектами вы можете заставить его работатьпотому что вы изменяете ссылку, а не объект:

>>> l = [0] * 4
>>> l[0] += 1
>>> l
[1, 0, 0, 0]

>>> l = [frozenset()] * 4
>>> l[0] |= set('abc')
>>> l
[frozenset(['a', 'c', 'b']), frozenset([]), frozenset([]), frozenset([])]

Но опять же, изменяемые объекты не годятся для этого, потому что операции на месте изменяют объект, а не ссылку:

l = [set()] * 4
>>> l[0] |= set('abc')    
>>> l
[set(['a', 'c', 'b']), set(['a', 'c', 'b']), set(['a', 'c', 'b']), set(['a', 'c', 'b'])]
23 голосов
/ 11 августа 2010

Itertools имеет функцию только для этого:

import itertools
it = itertools.repeat(e,n)

Конечно, itertools дает вам итератор вместо списка. [e] * n дает вам список, но, в зависимости от того, что вы будете делать с этими последовательностями, вариант itertools может быть гораздо более эффективным.

10 голосов
/ 27 января 2015

Как уже отмечали другие, использование оператора * для изменяемого объекта дублирует ссылки, поэтому, если вы измените один, вы измените их все. Если вы хотите создать независимые экземпляры изменяемого объекта, ваш синтаксис xrange - самый питонский способ сделать это. Если вас беспокоит наличие именованной переменной, которая никогда не используется, вы можете использовать анонимную переменную подчеркивания.

[e for _ in xrange(n)]
7 голосов
/ 11 августа 2010
[e] * n

должно работать

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...