2017-02-03 2 views
22

Я хочу отсортировать список по количеству вхождений элементов в списке.
Когда я использую эту форму:Сортировка списка по количеству вхождений элементов в списке

A=[2,1,3,4,2,2,3] 
A.sort(key=lambda x:A.count(x)) 
print(A) 

результат не то, что я хочу: [2, 1, 3, 4, 2, 2, 3].
Но, когда я пишу, как он с помощью sorted:

B=sorted(A,key=lambda x:A.count(x)) 
print(B) 

результат прав: [1, 4, 3, 3, 2, 2, 2].
В чем причина такого поведения?

+3

Сторона примечания, вам не нужно «лямбда», например. 'A.sort (key = A.count)' –

+0

Это возвращает количество событий для каждого элемента в A: '[A.count (element) для элемента в наборе (A)]' – UpSampler

+2

Использование 'Counter' (' A.sort (key = collections.Counter (A) .get)) здесь будет более эффективным и будет работать как для сортировки, так и для сортировки. – Faibbus

ответ

17

Это по дизайну и намеренно. CPython временно «запрещает» доступ к списку, пока список сортируется на месте, поведение documented here:

CPython деталь реализации:Хотя список сортируется, то эффект попытки мутировать , или даже проверить, список не определен. Реализация C на Python делает список пустым на время и повышает значение ValueError, если он может обнаружить, что список был мутирован во время сортировки.

Вы можете проверить, что при печати A внутри ключевой функции - вы получите пустой список:

In [2]: def key_function(x): 
    ...:  print(A, x) 
    ...:  return A.count(x) 
    ...: 

In [3]: A.sort(key=key_function) 
([], 2) 
([], 1) 
([], 3) 
([], 4) 
([], 2) 
([], 2) 
([], 3) 

Но, если вы сделаете это для sorted():

In [4]: sorted(A, key=key_function) 
([2, 1, 3, 4, 2, 2, 3], 2) 
([2, 1, 3, 4, 2, 2, 3], 1) 
([2, 1, 3, 4, 2, 2, 3], 3) 
([2, 1, 3, 4, 2, 2, 3], 4) 
([2, 1, 3, 4, 2, 2, 3], 2) 
([2, 1, 3, 4, 2, 2, 3], 2) 
([2, 1, 3, 4, 2, 2, 3], 3) 
Out[4]: [1, 4, 3, 3, 2, 2, 2] 

Это также задокументировано внутри sort() implementation:

/* The list is temporarily made empty, so that mutations performed 
* by comparison functions can't affect the slice of memory we're 
* sorting (allowing mutations during sorting is a core-dump 
* factory, since ob_item may change). 
*/. 
+5

это не потому, что документально подтверждено, что оно не сосать :) –

+2

Это ограничение, вероятно, не должно применяться к 'key =' функции. Я предлагаю подать отчет об ошибке на странице http://bugs.python.org/ – zwol

+2

Whoa! Нарушение принципа наименьшего удивления. Ошибка будет улучшением, imo. – jpmc26

6

Похоже, что A изменяется во время процесса сортировки на месте, поэтому вы не можете полагаться на значение A во время процесса сортировки.

Изготовление копии также работает.

A=[2,1,3,4,2,2,3] 
B=A[:] 
A.sort(key=lambda x:B.count(x)) 
print(A) 
+2

Я не уверен, что это полный ответ, похоже, больше похоже на предположение;) –

+1

Правильное предположение тогда :) –

2

Я считаю, что это потому, что A.sort модифицирует список на месте под при расчете. sorted() не изменяет список и поэтому возвращает правильный результат.

1

Встроенные sortedcreates a list out of the sequence при условии, а затем сортирует, что на основе ключевого аргумента (опуская проверку ошибок):

/* copy sequence provided */ 
newlist = PySequence_List(seq); 

/* get list.sort for the list object */ 
callable = _PyObject_GetAttrId(newlist, &PyId_sort); 

/* call it and then return later on */ 
v = _PyObject_FastCallKeywords(callable, args + 1, nargs - 1, kwnames); 

Это по существу приводит к чему-то вроде того, что Жан предусмотрено в своем ответе:

B = list(A) 
B.sort(key=lambda x: A.count(x)) 

Сделав эту копию B и ссылаясь на A в функции key, это устраняет ограничение n наложенным A.sort, который не может заглянуть в себя.

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