Python: Attrgetter, который обрабатывает значения None и может использоваться в цикле - PullRequest
2 голосов
/ 27 апреля 2020

У меня есть функция, которая несколько раз сортирует на основе списка кортежей атрибутов и обращенных логических значений, например:

def multisort(lst, sorting):
  for attr, reverse in reversed(sorting):
    lst.sort(key=operator.attrgetter(attr), reverse=reverse)
  return lst

Пример ввода в сортировку var будет выглядеть примерно так: [('attr_1', True), ('attr_2', False)].

Я обновляю код до Py3, и он больше не работает, потому что иногда значения атрибутов равны None (нельзя сравнивать NoneType с не-NoneTypes). Существует множество решений для стекопотока для этого типа проблем, которые рекомендуют заменять attrgetter для лямбда-функции, такой как lambda x: (getattr(x, attr) is None, getattr(x, attr)).

К сожалению, это не работает для меня, потому что атрибуты, по которым я сортирую, могут иметь точки в них, например, 'attr_1.sub_attr_1'. operator.attrgetter поддерживает это, но, конечно, нативный getattr этого не делает.

Любые предложения о том, как можно написать обертку вокруг attrgetter для этого или написать собственную функцию ключа, которая будет работать здесь? Заранее спасибо.

1 Ответ

1 голос
/ 27 апреля 2020

Вы можете использовать альтернативный метод получения, который будет возвращать ключевую функцию, которая будет генерировать кортежи (value is not None, value) (если вы хотите, чтобы None был первым, что было в случае с Python 2, где оно меньше всего).

import operator


def none_aware_attrgetter(attr):
    getter = operator.attrgetter(attr)
    def key_func(item):
        value = getter(item)
        return (value is not None, value)
    return key_func

def multisort(lst, sorting):
    for attr, reverse in reversed(sorting):
        lst.sort(key=none_aware_attrgetter(attr), reverse=reverse)
    return lst

Пример прогона:

class C:
    def __repr__(self):
        return f'<a1:{self.a1}, a2.s: {self.a2.s}>'

a = C()
b = C()
c = C()

a.a1 = 10
b.a1 = None
c.a1 = 0


a.a2 = C()
a.a2.s = 10

b.a2 = C()
b.a2.s = None

c.a2 = C()
c.a2.s = 0


lst = [a, b, c]
print(multisort(lst, [('a1', False)]))
# [<a1:None, a2.s: None>, <a1:0, a2.s: 0>, <a1:10, a2.s: 10>]

print(multisort(lst, [('a2.s', True)]))
# [<a1:10, a2.s: 10>, <a1:0, a2.s: 0>, <a1:None, a2.s: None>]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...