Python sorted custom key

Custom key function in Python3

I want to write a custom key function for a question I saw at Pramp. I am used to using something like:

my_list.sort(key = lambda x: (x[1], -x[0])) 

This time I need a more complicated key function, and I realized that I don’t know how to write it. How can I write such a key function for sorting in Python3? Which value should key function return? What should its input be? My key function (not working)

def key_function(num1, num2): if abs(num1) < abs(num2): return num1 if abs(num1) >abs(num2): return num2 if num1 < num2: return num1 if num1 >num2: return num2 if num1 == num2: return num1 

From Pramp: If two numbers have the same absolute value, sort them according to sign, where the negative numbers come before the positive numbers.

input: arr = [2, -7, -2, -2, 0] output: [0, -2, -2, 2, -7] 
 def compare(a, b): if abs(a) < abs(b): return -1 if abs(a) >abs(b): return 1 if a < b: return -1 if a >b: return 1 return 0 arr.sort(cmp = compare) return arr 

Python 3 does not support cmp functions directly anymore, but you can use functools.cmp_to_key() and use their pseudocode directly.

A key function is not a comparison function. A key function is only passed one element. When it’s used, python iterates over the entire list and runs the key function for each element. Then it uses the results of that key function to sort instead.

Читайте также:  Javascript border style width

Here, I’d just sort by absolute value and use the sign as tie breaker: lambda v: (abs(v), v >= 0)) . This produces (2, False) for -2 and (2, True) for 2, and so sorts -2 before 2 but after -1 or 0.

@RocketHazmat Thank a lot! If I were to write the key as a separate function (asking just to understand it better), as python iterates the list and runs the key function for each element, would the key function return a boolean? A special data type?

3 Answers 3

The key function needs to return a tuple with absolute values and a sortable indicator of the sign of the value.

  • abs() will give the absolute value
  • value >= 0 would do for something to sort by sign; negative values produce False , which sorts before True .

Translated to a lambda that’s:

my_list.sort(key=lambda v: (abs(v), v >= 0)) 

The key function produces:

 2 => (2, True) -7 => (7, False) -2 => (2, False) 0 => (0, True) 

which Python then uses to sort the list by. Since tuples are compared element by element and the first non-equal value determines their order, that means the False / True sign flag only is used when the first element is equal, e.g. when comparing (2, False) (-2) with (2, True) (2).

Just for the sake of completeness, I’d like to point out that overwriting the comparison method on a custom object is another way of customising a sort operation.

If the custom key function becomes too complex to handle, this can be a worthwhile approach.

Given a class defining one or more rich comparison ordering methods, this class decorator supplies the rest. This simplifies the effort involved in specifying all of the possible rich comparison operations:

The class must define one of __lt__(), __le__(), __gt__(), or __ge__(). In addition, the class should supply an __eq__() method.

For example:

@total_ordering class Student: def _is_valid_operand(self, other): return (hasattr(other, "lastname") and hasattr(other, "firstname")) def __eq__(self, other): if not self._is_valid_operand(other): return NotImplemented return ((self.lastname.lower(), self.firstname.lower()) == (other.lastname.lower(), other.firstname.lower())) def __lt__(self, other): if not self._is_valid_operand(other): return NotImplemented return ((self.lastname.lower(), self.firstname.lower()) < (other.lastname.lower(), other.firstname.lower())) 

Источник

Custom Python list sorting

The code works (and I wrote it some 3 years ago!) but I cannot find this thing documented anywhere in the Python docs and everybody uses sorted() to implement custom sorting. Can someone explain why this works?

sorted() and sort() offer custom sorting in much the same way, modulo the difference in calling convention.

Indeed, what happens is that using a key parameter is preferred over passing a cmp function. (The later is not even implemented in Python 3)

It's kind of ambiguous, depends on what the items in the list were; your code requires that they have an attribute foo , otherwise it blows up. Better to define a custom __lt__() method for your class, then sorted() and list.sort() will work out-of-the-box. (Btw, objects no longer need to define __cmp__() , just __lt__() . See this

6 Answers 6

As a side note, here is a better alternative to implement the same sorting:

alist.sort(key=lambda x: x.foo) 
import operator alist.sort(key=operator.attrgetter('foo')) 

Check out the Sorting How To, it is very useful.

The sort() method takes optional arguments for controlling the comparisons.

cmp specifies a custom comparison function of two arguments (list items) which should return a negative, zero or positive number depending on whether the first argument is considered smaller than, equal to, or larger than the second argument: cmp=lambda x,y: cmp(x.lower(), y.lower()). The default value is None.

Thanks miles82 I was checking here and couldn't see it in the method signature docs.python.org/tutorial/datastructures.html

I don't see the same text on the page you linked to. Did the documentation change. Besides, when I try to use cmp , I get TypeError: 'cmp' is an invalid keyword argument for this function . What is going on here?

@HelloGoodbye sort() doesn't have a cmp argument in Python 3. This is an old answer when the docs link was for Python 2. You can find the old docs here or read more about it here. If you're using Python 3, use the key argument instead.

And what if you actually want to provide a comparison function? I want to treat numbers in a string (of any length, picked out greedily) as symbols, equivalently to how individual characters are otherwise treated. I know how to achieve that trivially if I may provide a comparison function, but not if I must provide a key function. Why was this changed?

I guess it can still be achieved if each number contained in the string is encoded using an encoding that orders the numbers lexicographically, such as Levenshtein coding. But I consider this more as a workaround to the fact that sort doesn’t take a comparison function as argument in Python 3, and not as something that I actually would like to do.

Источник

Сортировка Python, Сортировка Python

Оптимизируйте свои подборки Сохраняйте и классифицируйте контент в соответствии со своими настройками.

Самый простой способ сортировки — это функция sorted(list), которая берет список и возвращает новый список с элементами в отсортированном порядке. Первоначальный список не изменился.

a = [5, 1, 4, 3] print(sorted(a)) ## [1, 3, 4, 5] print(a) ## [5, 1, 4, 3]

Чаще всего функция sorted() передает список, но на самом деле она может принимать в качестве входных данных любую итерируемую коллекцию. Более старый метод list.sort() является альтернативой, описанной ниже. Функцию sorted() кажется проще использовать по сравнению с sort(), поэтому я рекомендую использовать sorted().

Функцию sorted() можно настроить с помощью необязательных аргументов. Необязательный аргумент sorted() reverse=True, например sorted(list, reverse=True), выполняет сортировку в обратном порядке.

strs = ['aa', 'BB', 'zz', 'CC'] print(sorted(strs)) ## ['BB', 'CC', 'aa', 'zz'] (case sensitive) print(sorted(strs, reverse=True)) ## ['zz', 'aa', 'CC', 'BB']

strs = ['ccc', 'aaaa', 'd', 'bb'] print(sorted(strs, key=len)) ## ['d', 'bb', 'ccc', 'aaaa']

вызовы отсортированы по ключу = len

В качестве другого примера, указание «str.lower» в качестве ключевой функции — это способ заставить сортировку обрабатывать прописные и строчные буквы одинаково:

## "key" argument specifying str.lower function to use for sorting print(sorted(strs, key=str.lower)) ## ['aa', 'BB', 'CC', 'zz']

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

## Say we have a list of strings we want to sort by the last letter of the string. strs = ['xc', 'zb', 'yd' ,'wa'] ## Write a little function that takes a string, and returns its last letter. ## This will be the key function (takes in 1 value, returns 1 value). def MyFn(s): return s[-1] ## Now pass key=MyFn to sorted() to sort by the last letter: print(sorted(strs, key=MyFn)) ## ['wa', 'zb', 'xc', 'yd']

Для более сложной сортировки, такой как сортировка по фамилии, а затем по имени, вы можете использовать функции itemgetter или attrgetter, например:

from operator import itemgetter # (first name, last name, score) tuples grade = [('Freddy', 'Frank', 3), ('Anil', 'Frank', 100), ('Anil', 'Wang', 24)] sorted(grade, key=itemgetter(1,0)) # [('Anil', 'Frank', 100), ('Freddy', 'Frank', 3), ('Anil', 'Wang', 24)] sorted(grade, key=itemgetter(0,-1)) #[('Anil', 'Wang', 24), ('Anil', 'Frank', 100), ('Freddy', 'Frank', 3)]

метод сортировки()

В качестве альтернативы sorted() метод sort() в списке сортирует этот список в порядке возрастания, например, list.sort(). Метод sort() изменяет базовый список и возвращает None, поэтому используйте его следующим образом:

alist.sort() ## correct alist = blist.sort() ## Incorrect. sort() returns None

Вышеупомянутое является очень распространенным недоразумением с sort() - он *не возвращает* отсортированный список. Метод sort() должен вызываться для списка; он не работает ни с какой перечисляемой коллекцией (но функция sorted() выше работает с чем угодно). Метод sort() предшествует функции sorted(), поэтому вы, скорее всего, встретите его в старом коде. Методу sort() не нужно создавать новый список, поэтому он может быть немного быстрее, если элементы для сортировки уже находятся в списке.

Кортежи

Кортеж — это группа элементов фиксированного размера, например координата (x, y). Кортежи похожи на списки, за исключением того, что они неизменяемы и не меняют размер (кортежи не являются строго неизменными, поскольку один из содержащихся элементов может быть изменяемым). Кортежи играют своего рода роль «структуры» в Python — удобный способ передать небольшой логический набор значений фиксированного размера. Функция, которая должна возвращать несколько значений, может просто вернуть кортеж значений. Например, если бы я хотел иметь список трехмерных координат, естественным представлением Python был бы список кортежей, где каждый кортеж имеет размер 3, содержащий одну группу (x, y, z).

Чтобы создать кортеж, просто перечислите значения в круглых скобках, разделенные запятыми. «Пустой» кортеж — это просто пустая пара скобок. Доступ к элементам кортежа такой же, как и к списку — len(), [ ], for, in и т. д. работают одинаково.

tuple = (1, 2, 'hi') print(len(tuple)) ## 3 print(tuple[2]) ## hi tuple[2] = 'bye' ## NO, tuples cannot be changed tuple = (1, 2, 'bye') ## this works

Чтобы создать кортеж размера 1, за одиноким элементом должна следовать запятая.

Это забавный случай в синтаксисе, но запятая необходима, чтобы отличить кортеж от обычного случая помещения выражения в круглые скобки. В некоторых случаях вы можете опустить круглые скобки, и Python увидит по запятым, что вы подразумеваете кортеж.

Присвоение кортежа кортежу имен переменных одинакового размера присваивает все соответствующие значения. Если кортежи не одного размера, выдается ошибка. Эта функция работает и для списков.

(x, y, z) = (42, 13, "hike") print(z) ## hike (err_string, err_code) = Foo() ## Foo() returns a length-2 tuple

Список понятий (необязательно)

Понимание списков — это более продвинутая функция, которая удобна в некоторых случаях, но не требуется для упражнений, и ее не нужно изучать в первую очередь (т. е. вы можете пропустить этот раздел). Понимание списка — это компактный способ написать выражение, которое расширяется до всего списка. Предположим, у нас есть список чисел [1, 2, 3, 4], вот понимание списка для вычисления списка их квадратов [1, 4, 9, 16]:

nums = [1, 2, 3, 4] squares = [ n * n for n in nums ] ## [1, 4, 9, 16]

Синтаксис таков [ expr for var in list ] — выражение for var in list выглядит как обычный цикл for, но без двоеточия (:). Выражение слева от него оценивается один раз для каждого элемента, чтобы дать значения для нового списка. Вот пример со строками, где каждая строка преобразуется в верхний регистр с помощью '. ' добавлено:

strs = ['hello', 'and', 'goodbye'] shouting = [ s.upper() + '. ' for s in strs ] ## ['HELLO. ', 'AND. ', 'GOODBYE. ']

Вы можете добавить проверку if справа от цикла for, чтобы сузить результат. Тест if оценивается для каждого элемента, включая только те элементы, для которых тест верен.

Упражнение: list1.py

Чтобы попрактиковаться в изучении материала этого раздела, попробуйте выполнить более поздние задачи в list1.py , в которых используются сортировка и кортежи (см. Основные упражнения ).

Если не указано иное, контент на этой странице предоставляется по лицензии Creative Commons "С указанием авторства 4.0", а примеры кода – по лицензии Apache 2.0. Подробнее об этом написано в правилах сайта. Java – это зарегистрированный товарный знак корпорации Oracle и ее аффилированных лиц.

Последнее обновление: 2023-02-16 UTC.

Источник

Оцените статью