Сортировать повторяемое в Python 3.6


Я просматривал некоторые популярные вопросы на так, и один из них был как мне отсортировать словарь по значению?. Потому что питон 3.6 теперь дает нам dictс сохранением их структуры, я подумал, что этот вопрос идти. Вот мой ответ.

Я надеюсь, что эта функция облегчает сортировку повторяемое (кортеж, список или словарь). В последнем случае, можно сортировать либо по ключам или по значениям, и он может принимать числовое сравнение с учетом.

Когда вы попробуйте использовать sorted на повторяемое, который содержит, например, струны, а также ИНЦ, она будет выполнена. Конечно, вы можете заставить строковое сравнение с STR(). Однако, в некоторых случаях вы хотите сделать фактическое числовое сравнение, где 12 меньше 20 (чего не происходит в строке сравнения). Поэтому я придумал следующее. Если вы хотите, явные числовые сравнения вы можете использовать флаг num_as_num который постарается сделать точный числовой сортировки, пытаясь преобразовать все значения для поплавков. Если это удастся, он будет делать числовой сортировки, в противном случае он прибегнет к сравнению строк.

С нетерпением жду, чтобы увидеть, что можно улучшить, эффективность мудр, а также структура кода мудрый.

def sort_iterable(iterable, sort_on=None, reverse=False, num_as_num=False):
    def _sort(i):
      try:
        if num_as_num:
          if i is None:
            _sorted = sorted(iterable, key=lambda v: float(v), reverse=reverse)
          else:
            _sorted = dict(sorted(iterable.items(), key=lambda v: float(v[i]), reverse=reverse))
        else:
          raise TypeError
      except (TypeError, ValueError):
        if i is None:
          _sorted = sorted(iterable, key=lambda v: str(v), reverse=reverse)
        else:
          _sorted = dict(sorted(iterable.items(), key=lambda v: str(v[i]), reverse=reverse))

      return _sorted

    if isinstance(iterable, list):
      sorted_list = _sort(None)
      return sorted_list
    elif isinstance(iterable, tuple):
      sorted_list = tuple(_sort(None))
      return sorted_list
    elif isinstance(iterable, dict):
      if sort_on == 'keys':
        sorted_dict = _sort(0)
        return sorted_dict
      elif sort_on == 'values':
        sorted_dict = _sort(1)
        return sorted_dict
      elif sort_on is not None:
        raise ValueError(f"Unexpected value {sort_on} for sort_on. When sorting a dict, use key or values")
    else:
      raise TypeError(f"Unexpected type {type(iterable)} for iterable. Expected a list, tuple, or dict")

Обновление следующие ответы:

  • Поменял лямбда-выражения только float или str функция, где это применимо.
  • Поменял попробовать-кроме логики и как он взаимодействует с условные.
  • Вызывает исключение при использовании sort_on=None когда давать dict.
  • Убрали посредника присвоения переменных

Я не иду в обратную сторону-или вперед проблем с совместимостью. Если угодно, это более интересный проект по улучшению моей программы.

def sort_iterable(iterable, sort_on=None, reverse=False, num_as_num=False):
    def _sort(i):
        if num_as_num:
            try:
                if i is None:
                    return sorted(iterable, key=float, reverse=reverse)
                else:
                    return dict(sorted(iterable.items(), key=lambda v: float(v[i]), reverse=reverse))
            except TypeError:
                raise TypeError("Tried parsing as float but could not. Only use num_as_num when all items that need "
                                "to be sorted can be converted to float")
        else:
            if i is None:
                return sorted(iterable, key=str, reverse=reverse)
            else:
                return dict(sorted(iterable.items(), key=lambda v: str(v[i]), reverse=reverse))


    if isinstance(iterable, list):
        return _sort(None)
    elif isinstance(iterable, tuple):
        return tuple(_sort(None))
    elif isinstance(iterable, dict):
        if sort_on.lower() == 'keys':
            return _sort(0)
        elif sort_on.lower() == 'values':
            return _sort(1)
        else:
            raise ValueError(f"Unexpected value {sort_on} for sort_on. When sorting a dict, use keys or values")
    else:
        raise TypeError(f"Unexpected type {type(iterable)} for iterable. Expected a list, tuple, or dict")


633
5
задан 2 марта 2018 в 05:03 Источник Поделиться
Комментарии
2 ответа

С первого взгляда, следующий лямда упаковки

key=lambda v: float(v)

это просто бессмысленно.

key=float

достаточно. Вы также не нужны промежуточные переменные в разделах:

if isinstance(iterable, list):
return _sort(None)

Вы также не показывает ничего, если я использую dict С None как sort_on значение. Вы могли бы проверить внутри isinstance.. dict Раздел для него:

if sort_on not in ('keys', 'values'):  # does not take into account sort_on='KEYS' or sort_on='kEyS'
raise hell

или

if sort_on is None or sort_on.lower() not in ('keys', 'values'):
raise hell


У вас есть безотказный проверьте ValueError исключение. Я не думаю, что это хорошее поведение. Разработчик должен быть проинформирован о том, что ценности, которые они предоставили не float() или str() или все преобразования, так что они могут отсеять проблему вместо того, чтобы нежелательное поведение.

Если выше не вашего предполагаемого поведения, то игнорировать остальные следующие. В противном случае, вы можете использовать местных cast_to ссылка:

def _sort(i):
cast_to = float if num_as_num else str
cast_func = cast_to if i is None else lambda v: cast_to(v[i])
cast_wrap = type(iterable)
iter = iterable.items() if issubclass(cast_wrap, dict) else iterable
return cast_wrap(sorted(iter, key=cast_func, reverse=reverse))

Таким образом, Вы вернуть отсортированный iterable в типе она была первоначально предоставлена вам, что может даже быть подклассом dict/list и т. д.

5
ответ дан 3 марта 2018 в 01:03 Источник Поделиться

Про идею и дизайн

Питон путь из утки типизированные сортировки использовать sorted(). Что позволяет

sorted([1,3,2])
tuple(sorted((1,3,2)))
sorted(['1','3','2'], key=float)
tuple(sorted(('1','3','2'), key=float))

которая охватывает два из 3 типов float или оригинальный тип. он не требует


  • импорт некоторых функция ручной вязки

  • догадываясь о возможности, параметров

  • гадать о типе возвращаемого

  • гадать о глубине копия (мелкие, глубокие, или по ссылке)

  • гадать об исключениях

это понятно и читаемо и говорит все и не должны быть сделано любым другим способом.

Теперь о сортировке dict. Вы полагаетесь на 3.6 деталь реализации, которые будут гарантированы в 3.7, когда выйдет. всякий раз, когда вы сделать такую версию-трюк, вы должны проверить версию и либо выдать ошибку или реализовать запасной вариант (например, OrderedDict). Однако при 3.7 выйдет, там может быть своего рода для dict реализованы, а также.

Ваше решение, скорее всего, устареют, когда они становятся законными.

О кодировании

Определить внутреннюю функцию без причины введения много сложностей. В вашем внутреннем функция проверить существование внешнего i чтобы принять решение о возврате dictэто ужасно.

Вы смешиваете if и обработка исключений в if num_as_num: для управления потоком. ваш exceptп.-Это совершенно обычное дело.

Вы не должны реализовать молчком резервный для сортировки строки (или что-то типа), когда пользователь запросил сортировка по float. это полный провал, скрывая ошибки пользователей и представив ложные данные как обычный результат.


Редактировать

Один важный момент я пропустил на первый взгляд. Вы делаете сортировка дополнительно на float преобразование или по умолчанию str() представление. вторая-плохая идея, так как вы не можете сортировать сравниваемых объектов по их встроенные функции сравнения. вместо этого вы заставляете ул. представление, которое в плане сортировки бесполезно.

Так что вы не силу str но есть личности функции lambda x: x. str может быть и третий вариант для сортировки.

Чтобы ваш измененный код - Предположим, нам нужно написать функцию вроде этой.

если мы посмотрим на

def sort_iterable(iterable, sort_on=None, reverse=False, num_as_num=False):
# [...]
if isinstance(iterable, list):
return _sort(None)
elif isinstance(iterable, tuple):
return tuple(_sort(None))
elif isinstance(iterable, dict):
if sort_on.lower() == 'keys':
return _sort(0)
elif sort_on.lower() == 'values':
return _sort(1)
else:
raise ValueError(f"Unexpected value {sort_on} for sort_on. When sorting a dict, use keys or values")
else:
raise TypeError(f"Unexpected type {type(iterable)} for iterable. Expected a list, tuple, or dict")

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

def new_sort_iterable(iterable, sort_on_values=False, reverse=False, num_as_num=False):
# [...]
if isinstance(iterable, list):
return _sort(None)
elif isinstance(iterable, tuple):
return tuple(_sort(None))
elif isinstance(iterable, dict):
return _sort(1) if sort_on_values else _sort(0)
else:
raise TypeError(f"Unexpected type {type(iterable)} for iterable. Expected a list, tuple, or dict")

далее мы рассмотрим

def _sort(i):
if num_as_num:
try:
if i is None:
return sorted(iterable, key=float, reverse=reverse)
else:
return dict(sorted(iterable.items(), key=lambda v: float(v[i]), reverse=reverse))
except TypeError:
raise TypeError("Tried parsing as float but could not. Only use num_as_num when all items that need "
"to be sorted can be converted to float")
else:
if i is None:
return sorted(iterable, key=str, reverse=reverse)
else:
return dict(sorted(iterable.items(), key=lambda v: str(v[i]), reverse=reverse))

здесь вы повторяетесь, разница только в ключ. мы можем скрыть эту подробность, снова мы избавимся от явной обработки исключений

convert = float if num_as_num else lambda v: v # note: default sort by v, not by str(v)
def _sort(i):
if i is None:
return sorted(iterable, key=convert, reverse=reverse)
else:
return dict(sorted(iterable.items(), key=lambda v: convert(v[i]), reverse=reverse))

Кстати: Ваш код может поднять ValueError как хорошо, что идет через другие, чем TypeError

Хотя мы уменьшили сложность там еще это некрасиво if i: в внутренней функции. Я бы сказал, что мы улучшаем читабельность, избегая внутреннюю функцию и

def new_sort_iterable(iterable, sort_on_values=False, reverse=False, num_as_num=False):

convert = float if num_as_num else lambda v: v

if isinstance(iterable, list):
return sorted(iterable, key=convert, reverse=reverse)
elif isinstance(iterable, tuple):
return tuple(sorted(iterable, key=convert, reverse=reverse))
elif isinstance(iterable, dict):
i = {False:0, True:1}[sort_on_values]
return dict(sorted(iterable.items(), key=lambda v: convert(v[i]), reverse=reverse))
else:
raise TypeError(f"Unexpected type {type(iterable)} for iterable. Expected a list, tuple, or dict")

Теперь мы могли бы преобразовать параметр вместо num_as_num

def new_sort_iterable(iterable, sort_on_values=False, reverse=False, convert=lambda v: v):

и использовать его как

new_sort_iterable(['1','3','2'])
new_sort_iterable(['1','3','2'], convert=float)
new_sort_iterable(['1','3','2'], convert=str)

Еще есть комментарии отсутствуют ...

3
ответ дан 3 марта 2018 в 02:03 Источник Поделиться