Поскольку второй +
оценивается как оператор унарный плюс , такой как
'' + + ' new message'
= '' + (+' new message')
= '' + NaN
= NaN
В общем случае вы хотите написать свою функцию извлечения ключей для сортировки; только в особых (хотя и важных) случаях бывает так, что вы можете просто повторно использовать существующее вызываемое для извлечения ключей для вас или просто подключить пару существующих («быстрым и грязным» способом с помощью lambda
, поскольку нет встроенный способ выполнения композиции функций.
Если вам часто приходится выполнять эти два вида операций для извлечения ключей (получить элемент и вызвать метод для этого элемента), я предлагаю:
def combiner(itemkey, methodname, *a, **k):
def keyextractor(container):
item = container[itemkey]
method = getattr(item, methodname)
return method(*a, **k)
return keyextractor
, поэтому listofdicts.sort(key=combiner('name', 'lower'))
будет работать в вашем случае.
Обратите внимание, что в то время как чрезмерное обобщение имеет издержки, со вкусом и умеренным обобщением (оставляя ключ элемента, имя метода и аргументы метода, если таковые имеются , поскольку в этом случае определено время выполнения), как правило, имеет преимущества - одна общая функция, не более сложная, чем дюжина конкретных и специализированных (с экстрактором, методом вызова или и тем, и другим), будет проще в их коде) для поддержания (и, конечно же, намного проще повторного использования!).)
from functools import partial
def nested_funcs(*funcs):
return partial(reduce, lambda arg, func: func(arg), funcs)
sorted(list_of_dicts, key=nested_funcs(itemgetter('name'), str.strip, str.lower))
Вероятно, вы должны пойти с лямбдой ради удобочитаемости. Но как интересное исследование функций более высокого порядка, вот расширенная версия q-combinator в Python (также известная как компилятор странных птиц). Это позволяет вам создать новую функцию, составив две функции
def compose(inner_func, *outer_funcs):
if not outer_funcs:
return inner_func
outer_func = compose(*outer_funcs)
return lambda *args, **kwargs: outer_func(inner_func(*args, **kwargs))
from operator import itemgetter, methodcaller
name_lowered = compose(itemgetter('name'), methodcaller('lower'))
print(name_lowered( {'name': 'Foo'} ))
. Если вы измените определения внутреннего и внешнего в функции compose
, вы получите более традиционный b-combinator (bluebird). Мне больше нравится q-combinator из-за сходства с Unix-каналами.
Лично мне хотелось бы, чтобы в стандартной библиотеке Python было две функции (возможно, в functools):
def compose(*funcs):
"""
Compose any number of unary functions into a single unary
function.
>>> import textwrap
>>> str.strip(textwrap.dedent(compose.__doc__)) == compose(str.strip, textwrap.dedent)(compose.__doc__)
True
"""
compose_two = lambda f1, f2: lambda v: f1(f2(v))
return reduce(compose_two, funcs)
def method_caller(method_name, *args, **kwargs):
"""
Return a function that will call a named method on the
target object with optional positional and keyword
arguments.
>>> lower = method_caller('lower')
>>> lower('MyString')
'mystring'
"""
def call_method(target):
func = getattr(target, method_name)
return func(*args, **kwargs)
return call_method
Я реализовал их для собственного использования в jaraco.util.functools .
В любом случае, теперь ваш код достаточно ясен, самодокументирован и прочен (IMO).
lower = method_caller('lower')
get_name = itemgetter('name')
lowered_name = compose(lower, get_name)
list_of_dicts.sort(key=lowered_name)
Как насчет этого:
list_of_dicts.sort(key=lambda a: a['name'].lower())
def lower_getter(field):
def _getter(obj):
return obj[field].lower()
return _getter
list_of_dicts.sort(key=lower_getter(key_field))
Это решение будет использовать ваш языковой стандарт вашей системы, а в качестве бонуса он будет сортировать и другие персонажи в соответствии с текущей локалью (поместит «ü» после «u» в немецкий язык и т. д.).
from locale import setlocale, strxfrm, LC_ALL
import operator
# call setlocale to init current locale
setlocale(LC_ALL, "")
def locale_keyfunc(keyfunc):
def locale_wrapper(obj):
return strxfrm(keyfunc(obj))
return locale_wrapper
list_of_dicts.sort(key=locale_keyfunc(operator.itemgetter("name")))
Это, конечно, использует, что сортировка локали - это «естественный» вид пользовательского интерфейса, который вы хотите эмулировать с помощью .lower ().
Я поражен тем, что модуль locale
python неизвестен и не используется, он наверняка является важным компонентом в приложении, которое я пишу (переведен на несколько языков, но языковой модуль важен для того, чтобы даже получить модуль one вправо. Пример: V 'и' W ', так что вам нужно их сопоставить. locale
делает все это для вас.). В локали POSIX
(не по умолчанию) это вернет сортировку «a» после «Z».