75

フィールド名のインデックスを覚えていなくても、名前付きタプルのリストを並べ替えたい。私の解決策はかなり厄介なようで、誰かがもっとエレガントな解決策を持っていることを望んでいました。

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

ありがとう、ニック

4

5 に答える 5

96
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'))
于 2012-08-23T08:48:23.720 に答える
66
sorted(seq, key=lambda x: x.name)
sorted(seq, key=lambda x: x.age)
于 2012-08-23T08:50:12.423 に答える
14

@zenpoyはパフォーマンスを懸念していたため、ここで示した2つの選択肢の速度をテストしました。

テストスクリプト:

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

ラムダの使用は確かに遅くなりました。最大10%遅くなります。

編集

さらにテストすると、複数の属性を使用して並べ替えた場合の結果が示されます。同じ設定で次の2つのテストケースを追加しました。

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

ラムダはまだアンダーパフォームしていますが、それほどではありません。現在、約5%遅くなっています。

テストはPython3.6.0で行われます。

于 2017-05-27T17:28:15.880 に答える
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)))
于 2018-03-10T19:18:56.763 に答える
4

これは、一部の人にとっては少し「魔法」すぎるかもしれませんが、私は次のことに部分的です。

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

編集:これは、ビルトインをnamedtuple使用してアクセサーを実装することを前提としています。これは、そのようなプロパティの属性を利用するためです(ドキュメントを参照)。これは一部の実装ではまだ当てはまるかもしれませんが、CPythonはもはやそれを行わないようです。これは、 https://bugs.python.org/issue32492で参照されている最適化作業に関連していると思います(つまり、 3.8以降)。そのような脆弱性は、私が言及した「魔法」の代償です。確かにを使用することを約束していません。property()fgetnamedtupleproperty()

書くPerson.name.__get__ことはより良いです(実装変更の前後に機能します)が、よりわかりやすく書くよりも難解な価値がないかもしれません。lambda p: p.name

于 2019-01-10T07:39:48.993 に答える