Сортировка списка по количеству вхождений элементов в списке
Я хочу отсортировать список по количеству вхождений элементов в списке.
Когда я использую эту форму:
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]
.
в чем причина такого поведения?
Ответы
Ответ 1
Это по дизайну и намеренно. CPython временно "запрещает" доступ к списку, пока список сортируется на месте, поведение описано здесь:
Детализация реализации CPython: Пока список сортируется, эффект от попыток мутации или даже проверки, список undefined. Реализация 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()
:
/* 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).
*/.
Ответ 2
Кажется, что A
изменяется во время процесса сортировки на месте, поэтому вы не можете полагаться на значение A
во время процесса сортировки.
Создание копии также работает.
A=[2,1,3,4,2,2,3]
B=A[:]
A.sort(key=lambda x:B.count(x))
print(A)
Подтверждено этой строкой в документации по питону
Детали реализации CPython: во время сортировки списка эффект от попытки изменения или даже проверки списка не определен. Реализация C на Python делает список пустым на время и вызывает ValueError, если он может обнаружить, что список был видоизменен во время сортировки.
Ответ 3
Я верю, потому что A.sort
изменяет список, расположенный под ним во время вычислений. sorted()
не изменяет список и поэтому возвращает правильный результат.
Ответ 4
Встроенный sorted
создает список из предоставленной последовательности, а затем сортирует его на основе аргумента ключа (исключая ошибку проверки):
/* 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
, это устранит ограничение, налагаемое A.sort
, которое не может заглянуть само по себе.