Pythonic way to sorting list of namedtuples by field name

后端 未结 5 1407
耶瑟儿~
耶瑟儿~ 2020-12-05 01:21

I want to sort a list of named tuples without having to remember the index of the fieldname. My solution seems rather awkward and was hoping someone would have a more elegan

相关标签:
5条回答
  • 2020-12-05 01:55

    This might be a bit too 'magical' for some, but I'm partial to:

    # sort list by name
    print(sorted(seq, key=Person.name.fget))
    
    0 讨论(0)
  • 2020-12-05 02:01

    I tested the two alternatives given here for speed, since @zenpoy was concerned about performance.

    Testing script:

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

    Results:

    attrgetter_test_name 44.26793992166096
    attrgetter_test_age 31.98247099677627
    lambda_test_name 47.97959511074551
    lambda_test_age 35.69356267603864
    

    Using lambda was indeed slower. Up to 10% slower.

    EDIT:

    Further testing shows the results when sorting using multiple attributes. Added the following two test cases with the same setup:

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

    Results:

    attrgetter_test_both 92.80101586919373
    lambda_test_both 96.85089983147456
    

    Lambda still underperforms, but less so. Now about 5% slower.

    Testing is done on Python 3.6.0.

    0 讨论(0)
  • 2020-12-05 02:09
    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)]
    

    Sort list by name

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

    Sort list by age

    sorted(seq, key=attrgetter('age'))
    
    0 讨论(0)
  • 2020-12-05 02:12

    since nobody mentioned using itemgetter(), here how you do using 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)))
    
    0 讨论(0)
  • 2020-12-05 02:18
    sorted(seq, key=lambda x: x.name)
    sorted(seq, key=lambda x: x.age)
    
    0 讨论(0)
提交回复
热议问题