2014-01-09 3 views
11

Я хочу создать два списка listOfA и listOfB для хранения индексов A и B из другого списка.Понимание списков Python для создания нескольких списков

s=['A','B','A','A','A','B','B'] 

Вывод должен быть два списка

listOfA=[0,2,3,4] 
listOfB=[1,5,6] 

Я могу сделать это с помощью двух операторов.

listOfA=[idx for idx,x in enumerate(s) if x=='A'] 
listOfB=[idx for idx,x in enumerate(s) if x=='B'] 

Однако, я хочу сделать это только в одной итерации, используя только списки. Можно ли сделать это в одном заявлении? что-то вроде listOfA,listOfB=[--code goes here--]

+0

Почему? Вас беспокоит сложность 2 * N? O (2N) ≈ O (N). Я бы серьезно подумал о том, чтобы использовать только два генератора, которые написаны почти так же, как и ваши списки. – kojiro

+3

@kojiro: Никакая сложность здесь не проблема, я просто хочу изучить возможности python. – Heisenberg

+0

Возможный дубликат [Python: разбиение списка на основе условия?] (Http://stackoverflow.com/questions/949098/python-split-a-list-based-on-a-condition) –

ответ

31

Само определение определения списка состоит в том, чтобы произвести один объект списка. Ваши 2 объекта списка имеют разную длину; вам придется использовать побочные эффекты для достижения того, чего вы хотите.

Не используйте здесь списки. Просто используйте обычный цикл:

listOfA, listOfB = [], [] 

for idx, x in enumerate(s): 
    target = listOfA if x == 'A' else listOfB 
    target.append(idx) 

Это оставляет вам только один цикла для выполнения; это побьет любые два понимания списков, по крайней мере, пока разработчики не найдут способ сделать переписку списков, строят список в два раза быстрее, чем цикл с отдельными вызовами list.append().

Я бы выбрал этот день в соответствии с вложенным списком только, чтобы иметь возможность создавать два списка на одной строке. Как указано в Zen of Python:

Показатели удобочитаемости.

+0

@ Решение Martin выглядит лучше, поскольку оно повторяется только один раз –

+0

Является ли понимание списка (для создания единого списка) быстрее, чем создание списка, добавляя? – Heisenberg

+2

@Heisenberg: да, потому что Python может делать это со списком полностью в C. Не надоедливый стек Python толкает и всплывает, не ищет атрибутов '.append()'. Мы можем немного оптимизировать последнее (используйте 'A, B = listOfA.append, listOfB.append' вне цикла и повторно их используем), но вызов стека все равно будет медленнее, чем код C. –

10

Сортировка; ключ для генерации списка 2-элемент, который вы можете распаковывать:

listOfA, listOfB = [[idx for idx, x in enumerate(s) if x == c] for c in 'AB'] 

То есть, я думаю, что это довольно глупо делать это таким образом, явный цикл гораздо более удобным для чтения.

+7

Это все еще повторяется дважды , и сильно нечитабельно. –

+0

Он также сказал, что ему нужна единственная итерация, а не только одно утверждение (это повторяется дважды). – nmclean

5

Приятным подходом к этой проблеме является использование defaultdict. Как уже сказал @Martin, понимание списка не является правильным инструментом для создания двух списков. Использование defaultdict позволит вам создать сегрегацию с использованием одной итерации. Кроме того, ваш код не будет ограничен в любой форме.

>>> from collections import defaultdict 
>>> s=['A','B','A','A','A','B','B'] 
>>> listOf = defaultdict(list) 
>>> for idx, elem in enumerate(s): 
    listOf[elem].append(idx) 
>>> listOf['A'], listOf['B'] 
([0, 2, 3, 4], [1, 5, 6]) 
+1

Для двух ключей я бы поместил деньги на мое условное заявление, избивающее ваши вызовы 'hash (elem)'. –

+0

@MartijnPieters: Я не буду делать ставку на это с вами. Я просто предоставляю альтернативу, если OP хочет расширить эту идею по нескольким клавишам (элементам). – Abhijit

Смежные вопросы