Je veux trier une liste de tuples nommés sans avoir à me souvenir de l'index du nom de champ. Ma solution semble plutôt délicate et espérait que quelqu'un disposerait d'une solution plus élégante.
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'))))
Merci, 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)]
Trier la liste par nom
sorted(seq, key=attrgetter('name'))
Trier la liste par âge
sorted(seq, key=attrgetter('age'))
sorted(seq, key=lambda x: x.name)
sorted(seq, key=lambda x: x.age)
J'ai testé les deux alternatives données ici pour la vitesse, car @zenpoy était soucieux de ses performances.
Script de test:
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))
Résultats:
attrgetter_test_name 44.26793992166096
attrgetter_test_age 31.98247099677627
lambda_test_name 47.97959511074551
lambda_test_age 35.69356267603864
L'utilisation de lambda était en effet plus lente. Jusqu'à 10% plus lent.
MODIFIER:
Des tests supplémentaires montrent les résultats lors du tri utilisant plusieurs attributs. Ajout des deux cas de test suivants avec la même configuration:
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))
Résultats:
attrgetter_test_both 92.80101586919373
lambda_test_both 96.85089983147456
Lambda continue de sous-performer, mais moins. Maintenant environ 5% plus lent.
Les tests sont effectués sur Python 3.6.0.
puisque personne n'a mentionné utiliser itemgetter (), voici comment vous utilisez 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)))
C'est peut-être un peu trop "magique" pour certains, mais je suis partial:
# sort list by name
print(sorted(seq, key=Person.name.fget))