Python「インデクサとは違うのだよ、インデクサとは!」

リストから特定の要素以降のリストを得る関数を書いた。



2009.12.3 追記
最後に現れた特定の要素以降のリストを得る関数です。L[L.rindex(find)+1:] のようなイメージです。
でも、このコードもリストを何回も作っては捨てて良くなさそうなので、書き直したのを最後に追加しました。


文字列だと rindex メソッドがあるので、そこからスライスすればいいけどリストにはない。

def later(L, find):
    if L.count(find):
        L = L[::-1]
        L = L[:L.index(find)]
        return L[::-1]
    else: return L

L = [ "Marc Bolan", "David Bowie", "Mick Ronson",
      "Ian Hunter", "Morgan Fisher", "Mick Ronson",
      "Brian Ferry", "Brian Eno", "Phil Manzanera", "Andy Mackay" ]

print later(L, "Mick Ronson")  # [ "Brian Ferry", "Brian Eno", "Phil Manzanera", "Andy Mackay" ]

単純にリストを反転させて、そこから最初に見つかった要素までのリストを作成して、また反転しているだけ。



ところで、return の行とその前の行は一行でも書ける。

    return L[:L.index(find)][::-1]

スライスはリストを返すので、帰ってきたリストにさらにスライスを適用している。
二次元配列とかと見た目が似ているけど全然違う。インデクサとスライスは全然違う。


インデクサは配列の要素を返し、スライスはリストを返す。下の例では最初と最後の書き方が同じ意味になる。

>>> L = [ 0, 1, 2, 3, 4, 5 ]
>>> L[-1]
5
>>> L[-1:]
[5]
>>> L[-1:][0]
5


だから、意味は無いけどこういう書き方でリストを反転させることもできる。

    L = L[::-1][::-1][::-1]

ただし、スライスは新しいリストを作成するので、上の例では2つのリストが新しく作成されすぐ捨てられる。一見して分かる以上に非効率。



話はもどって、もうちょっと複雑なリストでも特定要素以降のリストを取得できるようにした。

def later(L, find, key=lambda x:x):
    for i, item in enumerate(L[::-1]):
        if key(item) == find:
            return L[len(L)-i:]
    return L


L = [
    { "Title":"Please Please Me",   "Release":1963, "Peak":1 },
    { "Title":"With The Beatles",   "Release":1963, "Peak":1 },
    { "Title":"A Hard Day's Night", "Release":1964, "Peak":1 },
    { "Title":"Beatles For Sale",   "Release":1964, "Peak":1 },
    { "Title":"HELP!",       "Release":1965, "Peak":1 },
    { "Title":"Rubber Soul", "Release":1965, "Peak":1 },
    { "Title":"Revolver",    "Release":1966, "Peak":1 },
    { "Title":"Sgt. Pepper's Lonely Hearts Club Band", "Release":1967, "Peak":1 },
    { "Title":"Magical Mystery Tour", "Release":1967, "Peak":1 },
    { "Title":"The Beatles",      "Release":1968, "Peak":1 },
    { "Title":"Yellow Submarine", "Release":1969, "Peak":1 },
    { "Title":"Abbey Road", "Release":1969, "Peak":1 },
    { "Title":"Let It Be",  "Release":1970, "Peak":1 },
    ]


print later(L, 1965, lambda x: x["Release"])

2009.12.3追加:スライスで反転したりするのを止めて、素直に負数のインデクサで探すようにした。
あと、トラックバックで教えたもらった itertools モジュールを使ってみた( countdown ってのが欲しいな)

import itertools

def later(L, find):
    if L.count(find):
        if L[-1] == find: return []
        for i in itertools.count(2):
            if L[-i] == find: return L[-i+1:]
    else: return L