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] )]

Он должен работать с любым типом данных (включая пользовательские классы)

Другие вопросы по тегам