2016/07/28

Python Tips:リストの tail を取得したい

Python でリストの tail 部分(先頭要素を除くすべての要素)を取得する方法をご紹介します。

いくつものアプローチが考えられるかと思いますが、代表的なものを 5 つほどあげてみます。

1. ふつうにループを回す方法


まず最初にふつうにループを回す方法を見てみましょう。

# 対象のリスト
l1 = ['mouse', 'cow', 'tiger', 'rabbit']

def tail1(list_original):
    """リストの tail を取得する( for ループを使用)。
    """
    list_tail = []
    for i, e in enumerate(list_original):
        if i > 0:
            list_tail.append(e)

    return list_tail

print(tail1(l1))
# => ['cow', 'tiger', 'rabbit']

どうということはないですね。「 Pythonic 」「関数型」なんていう考え方はさておいて、とりあえず書くとしたらこうなるでしょうか。もちろん Python らしくないのでこのパターンを使うのはやめましょう。

変数 l1 については以下の 2 つめ以降のサンプルでも使用します。

2. pop() メソッドを使う方法


続いて、リストの pop() メソッドを使って先頭の要素を削除する方法です。

def tail2(list_original):
    """リストの tail を取得する( pop() メソッドを使用)。

    オリジナルのリストの先頭要素が削除されるので要注意。
    """
    list_original.pop(0)
    return list_original

print(tail2(l1[:]))
# => ['cow', 'tiger', 'rabbit']

こちらはもともとのリストが破壊されてもよい(最初の要素がなくなってもよい)場合にのみ使用することができるパターンです。

3. スライスシンタックス [] を使用する方法


続いてスライスシンタックスを使う方法です。 target_list[1:] と書くことで、先頭以外のすべての要素をつかむことができます。

def tail3(list_original):
    """リストの tail を取得する(スライスシンタックスを使用)。
    """
    return list_original[1:]

print(tail3(l1))
# => ['cow', 'tiger', 'rabbit']

4. アンパック演算子を使用する方法


4 つめは * 演算子を使用する方法です。

def tail4(list_original):
    """リストの tail を取得する(アンパックのための * 演算子を使用)。
    """
    head, *tail = list_original
    return tail

print(tail4(l1))
# => ['cow', 'tiger', 'rabbit']

* を使えばきれいに head と tail を分割することができます。シンプルでわかりやすいですね。ちなみに head と tail に代入している行は次のように書くことも可能です。

[head, *tail] = list_original

ただしこちらは Python 3 限定の書き方であり、 Python 2 では使用することができません。 PEP 3132 で導入されました。

ちなみに、この「 * 」の演算子、 Ruby などでは splat (スプラット)演算子という名前が一般的ですが、 Python では公式な名前がついていないようです。。。 Ruby などにならって通称 splat 演算子で呼ぶのがわかりやすいでしょうか。

5. itertools.islice() を使用する方法


最後に itertools の islice() 関数を使う方法を見てみましょう。

import itertools

def tail5(list_original):
    """リストの tail を取得する( itertools.islice() を使用)。
    """
    return itertools.islice(list_original, 1, None)

print(list(tail5(l1)))
# => ['cow', 'tiger', 'rabbit']

islice() は iterable なオブジェクトを受け取り、その特定の区間を返すイテレータを返す関数です。

3 つの引数は前から順に「 iterable オブジェクト」「 start インデックス」「 stop インデックス」となります。 stop に None を渡すと無制限という意味になるので、この場合は「最初の 1 要素のみスキップして全要素取得する」という処理になります(結果をリストとして表示するために print() の前に list() でリスト化していますが、戻り値はイテレータです)。

もともとのリストが長い場合などにはこの islice() パターンを使うのがよいでしょう。

以上です。

パターン 1 2 はあくまでも比較用としてあげたものなので、通常使用するのはパターン 3 4 5 のいずれかがよいかと思います。


参考:

- PEP 3132 -- Extended Iterable Unpacking | Python.org
- proper name for python * operator? - Stack Overflow

0 件のコメント: