Manera de Pythonic para ordenar la lista de las muestras nombradas por nombre de campo

Quiero ordenar una lista de tuplas con nombre sin tener que recordar el índice del nombre de campo. Mi solución parece bastante incómoda y esperaba que alguien tuviera una solución más elegante.

from operator import itemgetter from collections import namedtuple Person = namedtuple('Person', 'name age score') seq = [ Person(name='nick', age=23, score=100), Person(name='bob', age=25, score=200), ] # sort list by name print(sorted(seq, key=itemgetter(Person._fields.index('name')))) # sort list by age print(sorted(seq, key=itemgetter(Person._fields.index('age')))) 

Gracias nick

 from operator import attrgetter from collections import namedtuple Person = namedtuple('Person', 'name age score') seq = [Person(name='nick', age=23, score=100), Person(name='bob', age=25, score=200)] 

Ordenar la lista por nombre

 sorted(seq, key=attrgetter('name')) 

Ordenar la lista por edad

 sorted(seq, key=attrgetter('age')) 
 sorted(seq, key=lambda x: x.name) sorted(seq, key=lambda x: x.age) 

Probé las dos alternativas dadas aquí para la velocidad, ya que @zenpoy estaba preocupado por el rendimiento.

Script de prueba:

 import random from collections import namedtuple from timeit import timeit from operator import attrgetter runs = 10000 size = 10000 random.seed = 42 Person = namedtuple('Person', 'name,age') seq = [Person(str(random.randint(0, 10 ** 10)), random.randint(0, 100)) for _ in range(size)] def attrgetter_test_name(): return sorted(seq.copy(), key=attrgetter('name')) def attrgetter_test_age(): return sorted(seq.copy(), key=attrgetter('age')) def lambda_test_name(): return sorted(seq.copy(), key=lambda x: x.name) def lambda_test_age(): return sorted(seq.copy(), key=lambda x: x.age) print('attrgetter_test_name', timeit(stmt=attrgetter_test_name, number=runs)) print('attrgetter_test_age', timeit(stmt=attrgetter_test_age, number=runs)) print('lambda_test_name', timeit(stmt=lambda_test_name, number=runs)) print('lambda_test_age', timeit(stmt=lambda_test_age, number=runs)) 

Resultados:

 attrgetter_test_name 44.26793992166096 attrgetter_test_age 31.98247099677627 lambda_test_name 47.97959511074551 lambda_test_age 35.69356267603864 

El uso de lambda fue de hecho más lento. Hasta un 10% más lento.

EDITAR :

Pruebas adicionales muestran los resultados al ordenar usando múltiples atributos. Se agregaron los siguientes dos casos de prueba con la misma configuración:

 def attrgetter_test_both(): return sorted(seq.copy(), key=attrgetter('age', 'name')) def lambda_test_both(): return sorted(seq.copy(), key=lambda x: (x.age, x.name)) print('attrgetter_test_both', timeit(stmt=attrgetter_test_both, number=runs)) print('lambda_test_both', timeit(stmt=lambda_test_both, number=runs)) 

Resultados:

 attrgetter_test_both 92.80101586919373 lambda_test_both 96.85089983147456 

Lambda todavía tiene un bajo rendimiento, pero menos. Ahora un 5% más lento.

La prueba se realiza en Python 3.6.0.

ya que nadie mencionó el uso de itemgetter (), aquí cómo lo hace usando itemgetter ().

 from operator import itemgetter from collections import namedtuple Person = namedtuple('Person', 'name age score') seq = [ Person(name='nick', age=23, score=100), Person(name='bob', age=25, score=200), ] # sort list by name print(sorted(seq, key=itemgetter(0))) # sort list by age print(sorted(seq, key=itemgetter(1))) 

Esto podría ser un poco “mágico” para algunos, pero soy parcial a:

 # sort list by name print(sorted(seq, key=Person.name.fget))