2010-11-02 4 views
8

Я должен взять список слов и отсортировать его, за исключением того, что мне нужно сгруппировать все строки, начинающиеся сначала с «x».Более питоновский способ написать это выражение?

Вот что я получил:

list_1 = [] 
list_2 = [] 

for word in words: 
    list_1.append(word) if word[0] == 'x' else list_2.append(word) 

return sorted(list_1) + sorted(list_2) 

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

EDIT

Пример: ['mix', 'xyz', 'apple', 'xanadu', 'aardvark'] урожайности ['xanadu', 'xyz', 'aardvark', 'apple', 'mix'].

ответ

41
>>> words = ['xoo', 'dsd', 'xdd'] 
>>> sorted(words, key=lambda x: (x[0] != 'x', x)) 
['xdd', 'xoo', 'dsd'] 

Объяснение: функциональная клавиша возвращает пару (кортеж). Первый элемент - False или True, в зависимости от того, является ли первый символ в строке 'x'. False сортирует до True, поэтому строки, начинающиеся с 'x', будут первыми в отсортированном виде. Второй элемент в кортежей будет использоваться для сравнения двух элементов, одинаковых для первого элемента, поэтому все строки, начинающиеся с 'x', будут отсортированы между собой, и все строки, не начинающиеся с 'x', будут отсортированы между собой.

+1

+1 +1, мне понадобился момент, чтобы понять. –

+0

Не понимаю, почему это работает. Всегда ли правда, что x? – helpermethod

+1

@Helper: он сравнивает кортежи, первый элемент которого либо «False», либо «True». Таким образом, все кортежи имеют «False» как первый элемент, предшествующий всем кортежам, которые имеют «True» как первый элемент. Когда первые элементы одинаковы, сравнение выполняется на основе второго элемента, который в этом случае также говорит о стандартном алфавитном порядке. – SilentGhost

6
words = ['xoo', 'dsd', 'xdd'] 
list1 = [word for word in words if word[0] == 'x'] 
list2 = [word for word in words if word[0] != 'x'] 
2
words = ['xoo', 'dsd', 'xdd'] 
list1=filter(lambda word:word[0]=='x',words) 
list2=filter(lambda word:word[0]!='x',words) 
1
>>> x = ['abc', 'xyz', 'bcd', 'xabc'] 
>>> y = [ele for ele in x if ele.startswith('x')] 
>>> y 
['xyz', 'xabc'] 
>>> z = [ele for ele in x if not ele.startswith('x')] 
>>> z 
['abc', 'bcd'] 
9

Во-первых: прекратите говорить «pythonic», когда вы имеете в виду «чистый». Это просто дрянное модное слово.

Не используйте такие терминологические выражения; он должен использоваться как часть выражения, а не как управление потоком. Это чище:

for word in words: 
    if word[0] == 'x': 
     list_1.append(word) 
    else: 
     list_2.append(word) 

Вы можете улучшить его немного больше - используя terniary выражения, как это прекрасно:

for word in words: 
    target = list_1 if word[0] == 'x' else list_2 
    target.append(word) 

Если words является контейнером, а не итератора, вы можете использовать:

list_1 = [word for word in words if word[0] == 'x'] 
list_2 = [word for word in words if word[0] != 'x'] 

Наконец, мы можем ломом все это, и вместо этого использовать два вида:

result = sorted(words) 
result = sorted(result, key=lambda word: word[0] != 'x') 

, который сначала сортируется нормально, а затем использует свойство stable для типов Python для перемещения слов, начинающихся с «x» на фронт, без изменения порядка.

+0

Кстати, хотя это объясняется недостатком, версия @ SilentGhost быстрее и чище, чем окончательный код. (Я предпочитаю свой ответ, потому что я думаю, что это более учебный, но, конечно, я предвзятый.) –

+7

На слове «pythonic»: есть интересная дискуссия в комментариях здесь: http://nedbatchelder.com/blog/201011/ pythonic.html –

+1

Я думаю, что представленное использование тернарного выражения совершенно справедливо. можно даже написать '(list_1, если слово [0] == 'x' else list_2) .append (word)', которое является более четким. – flow

2

Для повторной вариации SilenGhosts кода (не стесняйтесь копировать, SilentGhost) в качестве кода не командной строки журнала

notinorder = ['mix', 'xyz', '', 'apple', 'xanadu', 'aardvark'] 
print sorted(notinorder, key = lambda x: (not x.startswith('x'), x)) 
+0

Я считаю, что 'startswith' и' endswith' наиболее полезны при тестировании на множество условий (они принимают кортеж) или когда длина префикса не предопределена. – SilentGhost

+0

Преимущество в том, что я могу иметь дело с '' без исправления, например x или x [0]! = 'X', производительность я не знаю, но показатель удобочитаемости ... Ваше решение является своего рода автоматической почтой/распаковкой для сортировки по переводчик, аккуратный материал! Мог бы обобщить ... Спасибо за напоминание о точке кортежа, я видел кортеж, используемый с одной строкой, но забыл, что! –

0

больше вдоль линий исходного раствора:

 
l1=[] 
l2=[] 
for w in sorted(words): 
    (l1 if w[0] == 'x' else l2).append(w) 
l1.extend(l2) 
return l1 
5

Следует отметить, что в Python 2.4 добавлен sorted. Если вы хотите, чтобы более короткая версия была немного чище и несколько более обратной совместимости, вы можете использовать функциональность .sort() непосредственно от list.Следует также отметить, что пустые строки генерируют исключение при использовании в этом случае синтаксиса индексации массива x[0] (как и во многих примерах)..startswith() should be used instead, as is properly used in Tony Veijalainen's answer.

>>> words = ['mix', 'xyz', '', 'apple', 'xanadu', 'aardvark'] 
>>> words.sort(key=lambda x: (not x.startswith('x'), x)) 
>>> words 
['xanadu', 'xyz', '', 'aardvark', 'apple', 'mix'] 

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

>>> words = ['mix', 'xyz', '', 'apple', 'xanadu', 'aardvark'] 
>>> new_words = words[:] 
>>> new_words.sort(key=lambda x: (not x.startswith('x'), x)) 
>>> new_words 
['xanadu', 'xyz', '', 'aardvark', 'apple', 'mix'] 
>>> words 
['mix', 'xyz', '', 'apple', 'xanadu', 'aardvark'] 
Смежные вопросы