2009-09-08 58 views
6

我想通過字典鍵排序詞典列表,我不想區分大寫和小寫字符。python:combine sort-key-functions itemgetter and str.lower

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] 

應提供

Ali, Hans, klaus, Krishna, Paul, peter 

,而不是

klaus, peter, Ali, Hans, Krishna, Paul 

回答

10

在一般情況下,你將要編寫您的密鑰提取函數用於排序目的;只有在特殊的情況下(儘管重要),您可以重新使用現有的可調用鍵爲您提取密鑰,或者只需結合現有的一些(使用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'))將工作你的情況。

請注意,雖然過度泛化具有成本,雅緻和適度泛化(如果存在項目關鍵字,方法名稱和方法參數,在此情況下爲運行時確定的)通常具有優點 - 一個通用函數比十幾種特定的和專門的(比如提取器,調用方法,或者兩者,在他們的代碼中硬連線)更復雜,將更容易維護(當然,更容易重用!)。

2
def lower_getter(field): 
    def _getter(obj): 
     return obj[field].lower() 
    return _getter 

list_of_dicts.sort(key=lower_getter(key_field)) 
+0

和作爲一個加號,它會自動與兩個字節串和unicode字符串工作。 – nosklo 2009-09-08 15:09:14

12

如何:

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

我得到一個錯誤「沒有可重複的」 – chovy 2015-05-06 01:23:48

4

爲了便於閱讀,您可能應該使用lambda表達式。但作爲對高階函數的有趣研究,這裏是Python中q-combinator的擴展版本(也稱爲奇怪鳥類組合器)。這使您可以通過組合兩種功能

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-組合子(藍鳥)的定義創建一個新的功能。我更喜歡q-combinator,因爲它與unix管道相似。

4

該解決方案將使用您的系統語言環境,並且作爲獎勵,它還會根據當前語言環境對最終的其他字符進行排序(將德語語言環境中的「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()模擬的用戶界面「自然」排序。

我很驚訝,Python的locale模塊是未知的,未使用的,它肯定是在我寫的應用程序的重要組成部分(翻譯成多國語言,但語言環境模塊是重要的,甚至還讓一個模塊的權利。舉個例子:在瑞典的'V'和'W'排序相似,所以你必須整理它們。locale爲你做了這些。)。 在POSIX區域設置(非默認值)中,這將恢復爲在「Z」之後對「a」進行排序。

+0

這是一個很好的建議,只是改變keyfunc到: 高清keyfunc(DIC): 回報strxfrm(DIC [「名稱」]) – Francesco 2009-09-08 17:24:16

+0

弗朗西斯:現在它採用了更加個性化的工廠風格(雖然它可以專業化更快,但很少重要)。 – u0b34a0f6ae 2009-09-08 17:30:44

4

就個人而言,我希望有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) 
3
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)) 
相關問題