
In Python, puoi specificare una funzione o richiamabile per il parametro chiave nelle funzioni integrate sorted(), max(), min(), ecc.
In questo articolo vengono descritti i seguenti contenuti.
- Specificare una funzione incorporata per il parametro chiave
- Specificare un’espressione lambda o la propria funzione per il parametro chiave
- Specificare operator.mgetter() per il parametro chiave
- Specificare operator.atrgetter() per il parametro chiave
- Specificare operator.methodcaller() per il parametro chiave
- Confronto della velocità tra l’espressione lambda e operator.itemgetter().
Vedere anche l’articolo seguente per esempi di utilizzo del parametro chiave.
Specificare una funzione incorporata per il parametro chiave
Un semplice esempio di utilizzo del parametro key consiste nello specificare una funzione incorporata.
Per impostazione predefinita, in sorted(), gli elementi dell’elenco vengono confrontati e ordinati come sono.
l = [1, -3, 2]
print(sorted(l))
# [-3, 1, 2]
Se abs() che fornisce un valore assoluto è specificato per il parametro chiave, gli elementi vengono ordinati in base al valore assoluto di ciascun elemento.
Si noti che le parentesi () non sono necessarie quando una o un altro richiamabile viene specificato come argomento.
print(sorted(l, key=abs))
# [1, 2, -3]
La funzione specificata in chiave viene utilizzata solo nel confronto e gli elementi nel risultato rimangono gli stessi. Se vuoi applicare una funzione a un elemento e convertirlo, usa la comprensione degli elenchi.
l_abs = [abs(i) for i in l]
print(l_abs)
# [1, 3, 2]
print(sorted(l_abs))
# [1, 2, 3]
Lo stesso vale per il metodo sort() delle liste.
l.sort(key=abs)
print(l)
# [1, 2, -3]
Puoi anche specificare il parametro chiave in max() e min().
l = [1, -3, 2]
print(max(l))
# 2
print(max(l, key=abs))
# -3
print(min(l))
# -3
print(min(l, key=abs))
# 1
Si noti che la chiave è un parametro di sola parola chiave, quindi deve essere sempre specificato come chiave=xxx.
Gli esempi seguenti sono ordinati(), ma l’uso del parametro chiave è lo stesso per sort(), max(), min(), ecc.
Un altro esempio è il caso di un elenco di stringhe. Per regola predefinita, l’elenco è ordinato alfabeticamente, ma può essere ordinato in base al numero di caratteri specificando len() come parametro chiave.
l_str = ['bbb', 'c', 'aa']
print(sorted(l_str))
# ['aa', 'bbb', 'c']
print(sorted(l_str, key=len))
# ['c', 'aa', 'bbb']
Specificare un’espressione lambda o la propria funzione per il parametro chiave
È possibile specificare non solo le funzioni integrate, ma anche le espressioni lambda o le proprie funzioni definite con def per il parametro chiave.
Utilizzare un elenco bidimensionale (elenco di elenchi) come esempio.
Quando si confrontano gli elenchi, viene confrontato per primo il primo elemento.
l_2d = [[2, 10], [1, -30], [-3, 20]]
print(sorted(l_2d))
# [[-3, 20], [1, -30], [2, 10]]
Specificando max() per l’argomento chiave, gli elenchi vengono ordinati in base ai loro valori massimi.
print(sorted(l_2d, key=max))
# [[1, -30], [2, 10], [-3, 20]]
Se vuoi ordinare in base al valore assoluto massimo di ogni elenco, usa un’espressione lambda.
print(sorted(l_2d, key=lambda x: max([abs(i) for i in x])))
# [[2, 10], [-3, 20], [1, -30]]
Nota che non devi preoccuparti se il numero di elementi nell’elenco è piccolo, ma l’utilizzo essere in grado di ridurre l’espressione della memoria usando un’espressione del generatore per max().
print(sorted(l_2d, key=lambda x: max(abs(i) for i in x)))
# [[2, 10], [-3, 20], [1, -30]]
Puoi definire una funzione con def invece di un’espressione lambda e specificarla per chiave.
def max_abs(x):
return max(abs(i) for i in x)
print(sorted(l_2d, key=max_abs))
# [[2, 10], [-3, 20], [1, -30]]
Specificare operator.mgetter() per il parametro chiave
itemgetter() nell’operatore di libreria standard contiene un oggetto richiamabile che recupera un elemento dell’elenco o un valore del dizionario.
Puoi ordinare un elenco bidimensionale in base al valore di qualsiasi posizione (indice) con operator.itemgetter().
import operator
l_2d = [[2, 10], [1, -30], [-3, 20]]
print(sorted(l_2d, key=operator.itemgetter(1)))
# [[1, -30], [2, 10], [-3, 20]]
Poiché operator.itemgetter(xxx) restituisce un callable, specificalo come key=operator.itemgetter(xxx).
f = operator.itemgetter(1)
print(f([2, 10]))
# 10
print(operator.itemgetter(1)([2, 10]))
# 10
Puoi fare lo stesso con un’espressione lambda.
print(sorted(l_2d, key=lambda x: x[1]))
# [[1, -30], [2, 10], [-3, 20]]
operator.itemgetter() è più veloce dell’espressione lambda.
Alla fine viene visualizzata il risultato di un semplice confronto della velocità di elaborazione tra operator.itemgetter() e operator.itemgetter().
operator.itemgetter() può essere utilizzato anche per il dizionario dict.
Come esempio viene utilizzato un elenco di dizionari con una chiave comune. I dizionari non possono essere confrontati tra loro, quindi viene generato un errore per uso predefinito, ma operator.item può essere per ordinare l’elenco in base al valore della chiave data.
l_dict = [{'k1': 2, 'k2': 10}, {'k1': 1}, {'k1': 3}]
# print(sorted(l_dict))
# TypeError: '<' not supported between instances of 'dict' and 'dict'
print(sorted(l_dict, key=operator.itemgetter('k1')))
# [{'k1': 1}, {'k1': 2, 'k2': 10}, {'k1': 3}]
Si noti che viene generato un errore se viene incluso un dizionario senza la chiave specificata.
# print(sorted(l_dict, key=operator.itemgetter('k2')))
# KeyError: 'k2'
Puoi fare lo stesso con un’espressione lambda.
print(sorted(l_dict, key=lambda x: x['k1']))
# [{'k1': 1}, {'k1': 2, 'k2': 10}, {'k1': 3}]
Se il dizionario non ha la chiave specificata, puoi sostituirla con valore con il metodo get(). Vedi il seguente articolo.
Se vengono specificati più argomenti in operator.itemgetter(), viene restituita una tupla contenente il risultato di ciascuno.
l_dict = [{'k1': 2, 'k2': 'ccc'}, {'k1': 1, 'k2': 'ccc'}, {'k1': 2, 'k2': 'aaa'}]
print(operator.itemgetter('k1', 'k2')(l_dict[0]))
# (2, 'ccc')
Le tuple vengono anche confrontate in ordine dal primo elemento come le liste.
print(sorted(l_dict, key=operator.itemgetter('k1')))
# [{'k1': 1, 'k2': 'ccc'}, {'k1': 2, 'k2': 'ccc'}, {'k1': 2, 'k2': 'aaa'}]
print(sorted(l_dict, key=operator.itemgetter('k1', 'k2')))
# [{'k1': 1, 'k2': 'ccc'}, {'k1': 2, 'k2': 'aaa'}, {'k1': 2, 'k2': 'ccc'}]
print(sorted(l_dict, key=operator.itemgetter('k2', 'k1')))
# [{'k1': 2, 'k2': 'aaa'}, {'k1': 1, 'k2': 'ccc'}, {'k1': 2, 'k2': 'ccc'}]
Puoi anche fare lo stesso con un’espressione lambda.
print(sorted(l_dict, key=lambda x: (x['k1'], x['k2'])))
# [{'k1': 1, 'k2': 'ccc'}, {'k1': 2, 'k2': 'aaa'}, {'k1': 2, 'k2': 'ccc'}]
Specificare operator.atrgetter() per il parametro chiave
operator.attrgetter() richiama un oggetto richiamabile che recupera un attributo.
Usa un elenco di oggetti datetime.date come esempio. Puoi ottenere il giorno, il mese e l’anno con gli attributi giorno, mese e anno di datetime.date.
import datetime
l_dt = [datetime.date(2003, 2, 10), datetime.date(2001, 3, 20), datetime.date(2002, 1, 30)]
print(l_dt[0])
# 2003-02-10
print(l_dt[0].day)
# 10
f = operator.attrgetter('day')
print(f(l_dt[0]))
# 10
Per impostazione predefinita, sono ordinati per data, ma puoi ordinare in base a un attributo con operator.attrgetter().
print(sorted(l_dt))
# [datetime.date(2001, 3, 20), datetime.date(2002, 1, 30), datetime.date(2003, 2, 10)]
print(sorted(l_dt, key=operator.attrgetter('day')))
# [datetime.date(2003, 2, 10), datetime.date(2001, 3, 20), datetime.date(2002, 1, 30)]
se operator.attrgetter() sia più veloce, può essere eseguito anche con un’espressione lambda.
print(sorted(l_dt, key=lambda x: x.day))
# [datetime.date(2003, 2, 10), datetime.date(2001, 3, 20), datetime.date(2002, 1, 30)]
Specificare operator.methodcaller() per il parametro chiave
operator.methodcaller() richiama un oggetto richiamabile che chiama un metodo.
Usa il metodo find(), che indica la posizione di una determinata stringa, come esempio.
l_str = ['0_xxxxA', '1_Axxxx', '2_xxAxx']
print(l_str[0])
# 0_xxxxA
print(l_str[0].find('A'))
# 6
f = operator.methodcaller('find', 'A')
print(f(l_str[0]))
# 6
Per impostazione predefinita, è ordinato in ordine alfabetico, ma puoi ordinare in base ai risultati di operator.methodcaller().
print(sorted(l_str))
# ['0_xxxxA', '1_Axxxx', '2_xxAxx']
print(sorted(l_str, key=operator.methodcaller('find', 'A')))
# ['1_Axxxx', '2_xxAxx', '0_xxxxA']
se operator.attrgetter() sia più veloce, può essere eseguito anche con un’espressione lambda.
print(sorted(l_str, key=lambda x: x.find('A')))
# ['1_Axxxx', '2_xxAxx', '0_xxxxA']
Confronto della velocità tra l’espressione lambda e operator.itemgetter().
Questa sezione mostra i risultati di un semplice confronto di velocità tra le espressioni lambda e operator.itemgetter().
Usa un elenco di dizionari con una chiave comune (10000 elementi) come.
import operator
l = [{'k1': i} for i in range(10000)]
print(len(l))
# 10000
print(l[:5])
# [{'k1': 0}, {'k1': 1}, {'k1': 2}, {'k1': 3}, {'k1': 4}]
print(l[-5:])
# [{'k1': 9995}, {'k1': 9996}, {'k1': 9997}, {'k1': 9998}, {'k1': 9999}]
Si noti che il codice seguente utilizza il comando magico di Jupyter Notebook %%timeit e non funziona se come script Python.
%%timeit
sorted(l, key=lambda x: x['k1'])
# 1.09 ms ± 35 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%%timeit
sorted(l, key=operator.itemgetter('k1'))
# 716 µs ± 28.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%%timeit
sorted(l, key=lambda x: x['k1'], reverse=True)
# 1.11 ms ± 41.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%%timeit
sorted(l, key=operator.itemgetter('k1'), reverse=True)
# 768 µs ± 58.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%%timeit
max(l, key=lambda x: x['k1'])
# 1.33 ms ± 130 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%%timeit
max(l, key=operator.itemgetter('k1'))
# 813 µs ± 54.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%%timeit
min(l, key=lambda x: x['k1'])
# 1.27 ms ± 69.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%%timeit
min(l, key=operator.itemgetter('k1'))
# 824 µs ± 83.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
operator.itemgetter() è più veloce delle espressioni lambda per tutte le funzioni sorted(), max() e min().
Naturalmente, i risultati possono variare a seconda dell’ambiente e delle condizioni (numero di elementi, ecc.).