Python: list.sort() запрос, когда список содержит различные типы элементов
Привет Питонический мир. День 4 изучения Python 3.3, и я обнаружил странное свойство list.sort
,
Я создал список из пяти элементов: четыре строки с номером в середине. Пытаясь получить list.sort
на работу выдал ожидаемую ошибку из-за смешивания типов:
>>> list = ['b', 'a', 3, 'd', 'c']
>>> list.sort()
Traceback (innermost last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: int() < str()
>>> list
['b', 'a', 3, 'd', 'c']
Список не изменился.
Но затем я переместил число в конец, снова использовал list.sort и получил это:
>>> list = ['b', 'a', 'd', 'c', 3]
>>> list.sort()
Traceback (innermost last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: int() < str()
>>> list
['a', 'b', 'c', 'd', 3]
ОК, ошибка. Но список отсортировался сам, выбивая номер до конца. Я не смог найти объяснения этому на этом сайте или в Лангтангене. Есть ли какая-то основная причина такого поведения? Было бы полезно в какой-то ситуации?
5 ответов
Из документов Python 3:
Этот метод сортирует список на месте, используя только<сравнения между элементами. Исключения не подавляются - если какая-либо операция сравнения завершится неудачно, вся операция сортировки завершится неудачно (и список, скорее всего, останется в частично измененном состоянии).
Документы не гарантируют какого-либо конкретного поведения, но элементы, скорее всего, будут частично отсортированы. В любом порядке, в котором они находились, когда возникло исключение, и этот порядок может варьироваться в зависимости от реализации или, возможно, (но маловероятно) двух последующих запусков программы.
Если вы хотите попробовать отсортировать товары, не беспокоясь о неудачном повторном заказе, вы можете использовать sorted
встроенная функция, которая будет возвращать новый список, а не изменять оригинал.
>>> seq = ['b', 'a', 3, 'd', 'c']
>>> try:
... seq = sorted(seq) # if sorted fails, result won't be assigned
... except Exception: # you may only want TypeError
... pass
...
>>> seq
['b', 'a', 3, 'd', 'c'] # list unmodified
РЕДАКТИРОВАТЬ: обратиться ко всем, говоря что-то вроде
как только он видит два разных типа, он вызывает исключение
Я знаю, что вы, вероятно, знаете, что такого рода утверждения являются упрощением, но я думаю, что если не ясно, это вызовет путаницу.
Следующий пример состоит из двух классов A
а также B
которые поддерживают сравнение друг с другом через их соответствующие __lt__
методы. Это показывает список, смешанный из этих двух типов, отсортированных с list.sort()
и затем распечатывается в отсортированном порядке, без исключений:
class A:
def __init__(self, value):
self.a = value
def __lt__(self, other):
if isinstance(other, B):
return self.a < other.b
else:
return self.a < other.a
def __repr__(self):
return repr(self.a)
class B:
def __init__(self, value):
self.b = value
def __lt__(self, other):
if isinstance(other, A):
return self.b < other.a
else:
return self.b < other.b
def __repr__(self):
return repr(self.b)
seq = [A(10), B(2), A(8), B(16), B(9)]
seq.sort()
print(seq)
Выход этого:
[2, 8, 9, 10, 16]
не важно, чтобы вы понимали каждую деталь этого. Это просто для иллюстрации того, что список смешанных типов может работать с list.sort()
если все части есть
Я пишу ниже ответ, предполагая, что я знаю типы данных в списке, может быть неэффективным. Моя идея состоит в том, чтобы разбить данный список на подсписки на основе типа данных, после чего отсортировать каждый отдельный список и объединить.
input= ['b', 'a', 3, 'd', 'c']
strs = list(filter(lambda x : type(x) ==str,input))
ints = list(filter(lambda x: type(x) == int, input))
output = sorted(strs) + sorted(ints)
В этом нет ничего необычного. Просто sort()
не проверяйте, содержит ли список согласованные типы данных, вместо этого он пытается выполнить сортировку. Поэтому, когда ваш элемент находится в конце, он анализируется в последнее время, и поэтому алгоритм отсортировал часть списка, прежде чем обнаружил ошибку.
И нет - это бесполезно, так как сильно зависит от реализованного механизма сортировки.
Зависит от того, как данные должны быть отсортированы, но что-то вроде этого может работать
l = ['a',3,4,'b']
sorted([str(x) for x in l])
['3', '4', 'a', 'b']
Недавно я столкнулся с той же проблемой и не хотел приводить все к строке, поэтому я сделал это, надеюсь, это поможет :)
list = ["a", 1, False, None, "b", (1,3), (1, 'a'),(1, [None, False]), True, 3, False]
type_weights = {}
for element in list:
if type(element) not in type_weights:
type_weights[type(element)] = len(type_weights)
print(sorted(list, key=lambda element: (type_weights[type(element)], str(element))))
Он должен вернуть примерно следующее: ['a', 'b', 1, 3, False, False, True, None, (1, 'a'), (1, 3), (1, [None, False] )]
Он должен работать с любым типом данных (включая пользовательские классы)