简体   繁体   English

python:结合sort-key-functions itemgetter和str.lower

[英]python: combine sort-key-functions itemgetter and str.lower

I want to sort a list of dictionaries by dictionary key, where I don't want to distinguish between upper and lower case characters. 我想按字典键对字典列表进行排序,而我不想在其中区分大小写字符。

dict1 = {'name':'peter','phone':'12355'}
dict2 = {'name':'Paul','phone':'545435'}
dict3 = {'name':'klaus','phone':'55345'}
dict4 = {'name':'Krishna','phone':'12345'}
dict5 = {'name':'Ali','phone':'53453'}
dict6 = {'name':'Hans','phone':'765756'}
list_of_dicts = [dict1,dict2,dict3,dict4,dict5,dict6]

key_field = 'name'
list_of_dicts.sort(key=itemgetter(key_field))
# how to combine key=itemgetter(key_field) and key=str.lower? 
for list_field in list_of_dicts:
    print list_field[key_field]

should provide 应该提供

Ali, Hans, klaus, Krishna, Paul, peter

and not 并不是

klaus, peter, Ali, Hans, Krishna, Paul

这个怎么样:

list_of_dicts.sort(key=lambda a: a['name'].lower())

In the general case, you'll want to write your key-extraction function for sorting purposes; 在一般情况下,您将需要编写键提取函数以进行排序。 only in special (though important) cases it happens that you can just reuse an existing callable to extract the keys for you, or just conjoin a couple of existing ones (in a "quick and dirty" way using lambda , since there's no built-in way to do function composition). 仅在特殊(尽管很重要)的情况下,您可能会重复使用现有的可调用项来为您提取密钥,或者只是将几个现有的可调用项结合在一起(使用lambda的“快速而又肮脏的”方式,因为没有内置的以功能组合的方式)。

If you often need to perform these two kinds of operations for key extraction (get an item and call a method on that item), I suggest: 如果您经常需要执行这两种操作以进行键提取(获取项目并对该项目调用方法),我建议:

def combiner(itemkey, methodname, *a, **k):
  def keyextractor(container):
    item = container[itemkey]
    method = getattr(item, methodname)
    return method(*a, **k)
  return keyextractor

so listofdicts.sort(key=combiner('name', 'lower')) will work in your case. 因此listofdicts.sort(key=combiner('name', 'lower'))将在您的情况下工作。

Note that while excessive generalization has costs, tasteful and moderate generalization (leaving the item key, method name, and method arguments if any, as runtime-determined, in this case) generally has benefits -- one general function, not more complex than a dozen specific and specialized ones (with the extractor, method to call, or both, hardwired in their code), will be easier to maintain (and, of course, much easier to reuse!-). 请注意,虽然过度泛化会带来成本,但适度而适度的泛化(在这种情况下,由运行时确定,保留项目键,方法名称和方法参数,如果有的话)通常会带来好处-一个泛型函数,不会比十二个特定的和专用的代码(在其代码中带有提取器,调用方法,或两者​​都硬连接),将更易于维护(当然,也更易于重用!-)。

You probably should go with a lambda for the sake of readability. 为了便于阅读,您可能应该使用lambda。 But as an interesting study into higher order functions, here's the extended version of q-combinator in Python (also known as the queer bird combinator). 但是,作为对高阶函数的有趣研究,以下是Python中q-combinator的扩展版本(也称为queer bird组合器)。 This allows you to create a new function by composing two functions 这使您可以通过组合两个函数来创建新函数

 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'} ))

If you reverse the definitions of inner and outer in the compose function, you get the more traditional b-combinator (bluebird). 如果在compose函数中反转inner和compose定义,则会得到更传统的b组合器(bluebird)。 I like the q-combinator more because of the similarity to unix pipes. 我更喜欢q组合器,因为它与unix管道相似。

This solution will use your system locale, and as a bonus, it will sort eventual other characters according to the current locale as well (Will put "ü" after "u" in a german locale etc). 此解决方案将使用您的系统语言环境,并且作为奖励,它还将根据当前语言环境对其他字符进行排序(在德语语言环境中,将“ü”放在“ 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")))

This of course uses that the locale sort is the User interface "natural" sort that you wish to emulate with .lower(). 当然,这使用的是语言环境排序,即您希望使用.lower()模拟的用户界面“自然”排序。

I'm amazed that python's locale module is unknown and unused, it for sure is an important component in the application I write (translated to multiple languages, but the locale module is important for even getting one module right. Case in point: in swedish 'V' and 'W' sort alike, so you have to collate them. locale does all that for you.). 我很惊讶python的locale模块是未知且未使用的,它肯定是我编写的应用程序中的重要组件(翻译成多种语言,但是语言环境模块对于甚至正确使用一个模块也很重要。) “ V”和“ W”类似,因此您必须对它们进行排序。 locale会为您完成所有操作。) In the POSIX locale (not default), this will revert to sorting "a" after "Z". POSIX语言环境(非默认)中,这将恢复为在“ Z”之后对“ a”进行排序。

Personally, I wish there were two functions in the Python standard library (probably in functools): 我个人希望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

I have implemented these for my own use in jaraco.util.functools . 我已经在jaraco.util.functools中实现了这些功能供我自己使用。

Either way, now your code is quite clear, self-documented, and robust (IMO). 无论哪种方式,现在您的代码都非常清晰,具有自说明性,并且功能强大(IMO)。

lower = method_caller('lower')
get_name = itemgetter('name')
lowered_name = compose(lower, get_name)

list_of_dicts.sort(key=lowered_name)
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))
def lower_getter(field):
    def _getter(obj):
        return obj[field].lower()
    return _getter

list_of_dicts.sort(key=lower_getter(key_field))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM