Подтвердить что ты не робот

Сортировка списка по количеству вхождений элементов в списке

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

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].
в чем причина такого поведения?

4b9b3361

Ответ 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, которое не может заглянуть само по себе.