Pythonic 按字段名称对命名元组列表进行排序的方法

Posted

技术标签:

【中文标题】Pythonic 按字段名称对命名元组列表进行排序的方法【英文标题】:Pythonic way to sorting list of namedtuples by field name 【发布时间】:2012-08-18 17:58:43 【问题描述】:

我想对命名元组列表进行排序,而不必记住字段名的索引。我的解决方案似乎很尴尬,希望有人能提供更优雅的解决方案。

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

谢谢, 尼克

【问题讨论】:

字段名称是始终作为字符串给出还是@clyfish 的解决方案也有效? 我并没有尝试做任何动态的事情,所以这两种解决方案都能完美运行。 【参考方案1】:
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)]

按名称排序列表

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

按年龄排序

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

【讨论】:

【参考方案2】:
sorted(seq, key=lambda x: x.name)
sorted(seq, key=lambda x: x.age)

【讨论】:

我认为这比使用attrgetter更优雅 我更喜欢 attrgetter,但这只是品味。如果我要让字段动态排序,还有一个优点。然后我可以传递字符串。 @zenpoy 请记住,attrgetter 的表现要好得多,而 lambdas 通常被认为不优雅 sorted(seq, key=lambda x: [x.age, x.name]) 按多个属性排序【参考方案3】:

我测试了这里给出的两个替代方案的速度,因为@zenpoy 关心性能。

测试脚本:

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

结果:

attrgetter_test_name 44.26793992166096
attrgetter_test_age 31.98247099677627
lambda_test_name 47.97959511074551
lambda_test_age 35.69356267603864

使用 lambda 确实比较慢。最多减慢 10%。

编辑

进一步的测试显示了使用多个属性进行排序时的结果。添加了以下两个具有相同设置的测试用例:

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

结果:

attrgetter_test_both 92.80101586919373
lambda_test_both 96.85089983147456

Lambda 仍然表现不佳,但不那么好。现在慢了大约 5%。

测试在 Python 3.6.0 上完成。

【讨论】:

感谢多属性排序案例:)【参考方案4】:

由于没有人提到使用 itemgetter(),这里你如何使用 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)))

【讨论】:

【参考方案5】:

这对某些人来说可能有点太“神奇”,但我偏爱:

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

编辑:这假定namedtuple 使用内置的property() 来实现访问器,因为它利用了此类属性(see documentation)上的fget 属性。在某些实现中这可能仍然是正确的,但似乎 CPython 不再这样做,我认为这与 https://bugs.python.org/issue32492 中引用的优化工作有关(所以,从 3.8 开始)。这种脆弱性就是我提到的“魔法”的代价; namedtuple 当然不承诺使用property()

Person.name.__get__ 更好(在实现更改之前和之后工作)但可能不值得奥秘而不是更清楚地写成lambda p: p.name

【讨论】:

您能否在答案中添加一些上下文?我试过了,但得到 AttributeError: '_collections._tuplegetter' object has no attribute 'fget' 当然。这种方法曾经有效,但它依赖于 namedtuple 的特定实现细节。这似乎在bugs.python.org/issue32492 中有所改变。不久将添加对答案的编辑。

以上是关于Pythonic 按字段名称对命名元组列表进行排序的方法的主要内容,如果未能解决你的问题,请参考以下文章

如何按两个元素对元组列表进行排序?

python 按升序对列表(或元组列表)进行排序

对具有名称的元组列表进行排序[重复]

将CSV文件数据读取为命名元组行的pythonic方法是啥?

如何使用字段名称的变量访问命名元组的字段?

按升序排序元组列表