2009-09-08 3 views
6

Ich möchte eine Liste von Wörterbüchern nach Wörterbuchschlüssel sortieren, wobei ich nicht zwischen Groß- und Kleinbuchstaben unterscheiden möchte.python: kombinieren sort-key-funktionen itemgetter und 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] 

sollte

Ali, Hans, klaus, Krishna, Paul, peter 

und nicht

klaus, peter, Ali, Hans, Krishna, Paul 

Antwort

10

Im allgemeinen Fall, Sie Ich möchte Ihre Schlüsselextraktionsfunktion für Sortierzwecke schreiben; nur in speziellen (wenn auch wichtigen) Fällen passiert es, dass Sie einfach eine vorhandene Callable wiederverwenden können, um die Schlüssel für Sie zu extrahieren, oder einfach nur ein paar existierende verbinden (auf "schnell und schmutzig" Weise mit lambda, da es keine eingebaute in der Weise, Funktionszusammensetzung zu tun).

Wenn Sie häufig diese beiden Arten von Operationen für Schlüssel Extraktion ausführen müssen (ein Element erhalten und ein Verfahren an diesem Punkt nennen), schlage ich vor:

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')) wird in Ihrem Fall arbeiten.

Beachten Sie, dass während übermäßige Generalisierung Kosten hat, geschmackvolle und moderate Verallgemeinerung (den Objektschlüssel, Methodennamen und Methodenargumente, wenn in diesem Fall als Laufzeit-bestimmt, hat im Allgemeinen Vorteile - eine allgemeine Funktion, nicht komplexer als ein Dutzend spezifischer und spezialisierter (mit dem Extraktor, Methode zu rufen, oder beides, in ihrem Code fest verdrahtet), wird einfacher zu pflegen sein (und natürlich viel einfacher wiederzuverwenden! -).

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

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

und als Plus, wird es automatisch mit beiden Strings bytestrings und Unicode arbeiten. – nosklo

12

liefern Wie wäre es damit:

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

Ich erhalte eine Fehlermeldung "Nicht iteratable" – chovy

4

Sie sollten wahrscheinlich mit einem Lambda aus Gründen der Lesbarkeit gehen. Aber als eine interessante Studie zu Funktionen höherer Ordnung, hier ist die erweiterte Version von q-Kombinator in Python (auch bekannt als der Queer Bird-Kombinator). Auf diese Weise können Sie mit dem Komponieren zwei Funktionen

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

eine neue Funktion erstellen Wenn Sie die Definitionen der inneren und äußeren in der compose Funktion umkehren, können Sie die traditionellere b-combinator (drossel) erhalten. Ich mag den q-Kombinator mehr wegen der Ähnlichkeit zu Unix-Pipes.

4

Diese Lösung verwendet Ihr Systemgebietsschema, und als einen Bonus wird es eventuelle andere Zeichen entsprechend dem aktuellen Gebietsschema ebenfalls sortieren (Wird "ü" nach "u" in einem deutschen Gebietsschema usw.).

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"))) 

Dies natürlich verwendet, dass die locale Art ist die Benutzeroberfläche „natürliche“ Art, die Sie mit .unterhalb nacheifern möchten().

Ich bin erstaunt, dass die Python locale Modul unbekannt und nicht genutzt wird, ist es sicher ist ein wichtiger Bestandteil in der Anwendung I (übersetzt in mehrere Sprachen zu schreiben, aber das locale-Modul ist wichtig für selbst ein Modul richtig hinzubekommen. Fall in Punkt: in schwedisch 'V' und 'W' gleich, so müssen Sie sie zusammenstellen. locale macht all das für Sie.). Im Gebietsschema POSIX (nicht Standard) wird dies auf "a" nach "Z" zurückgesetzt.

+0

Dies ist ein schöner Vorschlag, nur keyfunc ändern: def keyfunc (dic): return strxfrm (dic [ "name"]) – Francesco

+0

Francesco: Jetzt verwendet er eine anpassbare Fabrik Stil (obwohl es spezialisiert sein kann, um schneller zu sein, ist es selten wichtig). – u0b34a0f6ae

4

Persönlich wünsche ich dort (in functools wahrscheinlich) in der Python-Standardbibliothek zwei Funktionen waren:

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 

ich diese für meinen eigenen Gebrauch in jaraco.util.functools umgesetzt haben.

Wie auch immer, jetzt ist Ihr Code ziemlich klar, selbstdokumentiert und robust (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)) 
Verwandte Themen