Pythonでリストや文字列を逆順にする(reversed, スライス操作)

Python 組込み型 組込み関数

リストやタプル、文字列等のシーケンス型の要素を逆順に取り出す方法として、下記2つが挙げられます。

  • 標準組み込み関数:reversed()を使う
  • スライス操作を使う

本記事では、これらの使いかたについて具体例を用いて丁寧に解説します。
また、最後に両者の処理効率についても確認してみました。

標準組み込み関数:reversed()による逆順

標準組込み関数reversed() は、引数seqの要素を逆順に並び替えたイテレータ を返します。
使い方は下記です。

reversed(seq)

以下に、組込み型のシーケンスそれぞれについて具体例を示します。
各例においては、逆順になっていることをfor文を用いて確認しています。

リストの場合

>>> a = ['a', 'b', 'c', 'd', 'e']
>>> reversed(a)
<list_reverseiterator object at 0x7f569792c790> #イテレータオブジェクトを返す

# イテレータなので、for文でも使えます
# 逆順で取り出されているのがわかります。
>>> for i in reversed(a):
...     print(i)
... 
e
d
c
b
a

# 元の値は変更ありません
>>> a
['a', 'b', 'c', 'd', 'e']

タプルの場合

>>> a = ('a', 'b', 'c', 'd', 'e')
>>> reversed(a)
<reversed object at 0x7f569792cb90>  #イテレータオブジェクトを返す

# 逆順で取り出されています。
>>> for i in reversed(a):
...     print(i)
... 
e
d
c
b
a

文字列の場合

>>> s = 'abcde'
>>> reversed(s)
<reversed object at 0x7f569792c910>  #イテレータオブジェクトを返す

# 逆順で取り出されています。
>>> for i in reversed(s):
...     print(i)
... 
e
d
c
b
a

# 反転した文字列を作るには、join()を使うのが良いですね
>>> ''.join(reversed(s))
'edcba'

rangeの場合

>>> r = range(5)
>>> reversed(r)
<range_iterator object at 0x7f56979a5270>  #イテレータオブジェクトを返す

# 逆順で取り出されています。
>>> for i in reversed(r):
...     print(i)
... 
4
3
2
1
0

ちなみに、引数seqは、
➀ __reversed()__メソッドを持つ、または
② シーケンス型プロトコル( __len__( )および __getitem__() )をサポートするオブジェクト
である必要があります。

どういうことか、実際に組み込み型のシーケンス型(リスト、タプル、文字列、range)について、dir() 関数を使って実際に確認してみます。
※)dir()関数は引数に設定したオブジェクトがどんなメソッドや属性を持っているかを調べることが出来ます。

  • リストの場合
  • 上記➀②どちらの条件も満たしています。

    >>> a = ['a', 'b', 'c', 'd', 'e']
    >>> '__reversed__' in dir(a)
    True
    
    >>> '__len__' in dir(a) and '__getitem__' in dir(a)
    True
  • タプルの場合
  • 上記②の条件を満たしています。

    >>> a = ('a', 'b', 'c', 'd', 'e')
    >>> '__reversed__' in dir(a)
    False
    
    >>> '__len__' in dir(a) and '__getitem__' in dir(a)
    True
  • 文字列の場合
  • 上記②の条件を満たしています。

    >>> s = 'abcde'
    >>> '__reversed__' in dir(s)
    False
    
    >>> '__len__' in dir(s) and '__getitem__' in dir(s)
    True
  • rangeの場合
  • 上記➀②の条件を満たしています。

    >>> r = range(5)
    >>> '__reversed__' in dir(r)
    True
    
    >>> '__len__' in dir(r) and '__getitem__' in dir(r)
    True
  • 集合(set)の場合
  • どちらの条件も満たしていません。

    >>> a = {'a', 'b', 'c', 'd', 'e'}
    >>> '__len__' in dir(a) and '__getitem__' in dir(a)
    False
    
    >>> '__reversed__' in dir(a)
    False

    よって、集合型はreversed()で使えません。

    >>> a = {'a', 'b', 'c', 'd', 'e'}
    >>> reversed(a)
    Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
    TypeError: 'set' object is not reversible

スライス操作による逆順

シーケンス型(リスト、タプル、文字列、range)はスライスをサポートしています。
よって、スライス操作でa[::-1]と設定することで逆順に並び替えることができます。
返り値は、逆順に並び替えられた新しいシーケンスです。元のデータは変更ありません。
参考記事)【Python】スライスの使いかたの基本

上記同様に、組込み型のシーケンスそれぞれについて具体例を示します。

  • リストの例
  • >>> a = ['a', 'b', 'c', 'd', 'e']
    >>> a[::-1]
    ['e', 'd', 'c', 'b', 'a']
  • タプルの場合
  • >>> a = ('a', 'b', 'c', 'd', 'e')
    >>> a[::-1]
    ('e', 'd', 'c', 'b', 'a')
  • 文字列の場合
  • >>> s = 'abcde'
    >>> s[::-1]
    'edcba'
  • range()の場合
  • >>> r = range(5)
    >>> r
    range(0, 5)
    
    >>> r[::-1]
    range(4, -1, -1) 
    
    # listに変換して確認
    >>> list(r)
    [0, 1, 2, 3, 4]
    
    >>> list(r[::-1])
    [4, 3, 2, 1, 0]

それぞれの方法の比較

最後に、上記2つの方法について処理速度メモリ消費量を下記のコードを用いて比較してみました。(コード全体はこちら ))

比較用コード:

for n in range(1, 7):
    # 検証用として、1〜10^n(n = 2, 3, 4, 5, 6)までの整数値を要素としたリストを作成
    r = list(range(n))

    # それぞれの方法について処理時間とメモリサイズを実測 
    t0 = time.perf_counter()
    rev01 = reversed(r) 
    t1 = time.perf_counter()
    rev02 = r[::-1]
    t2 = time.perf_counter()

    # reversed()の処理時間
    dt1 = t1-t0 

    # スライス操作の処理時間
    dt2 = t2-t1
    
    # reversed()の場合のメモリサイズ
    size01 = sys.getsizeof(rev01)

    # スライス操作時のメモリサイズ
    size02 = sys.getsizeof(rev02)

結果を下記に示します。

-- データ数:10^2 --
reversed(): time: 0.0000028290(s), size: 64(bytes)
slice     : time: 0.0000027260(s), size: 872(bytes)

-- データ数:10^3 --
reversed(): time: 0.0000021380(s), size: 64(bytes)
slice     : time: 0.0000124370(s), size: 8072(bytes)

-- データ数:10^4 --
reversed(): time: 0.0000025570(s), size: 64(bytes)
slice     : time: 0.0000906550(s), size: 80072(bytes)

-- データ数:10^5 --
reversed(): time: 0.0000059690(s), size: 64(bytes)
slice     : time: 0.0008673030(s), size: 800072(bytes)

-- データ数:10^6 --
reversed(): time: 0.0000098930(s), size: 64(bytes)
slice     : time: 0.0095510020(s), size: 8000072(bytes)

reversed()の方が処理時間が短く、メモリ消費量も少ないです。イテレータを生成するだけなので当然の結果ですね。
データを逆順にした結果そのものが最終的な出力結果となる場合は別ですが、逆順にした結果を別の処理に使う場合はreversed()でイテレータへ変換したほうが効率的になると思います。

確認した環境

  • OS: Ubuntu18.04LTS
  • Python3.7.4
  • まとめ

    シーケンスの要素を逆順にする方法について、標準組込み関数reversed()を使う方法、およびスライス操作を使う方法についてまとめました。

Learn more...

書籍でもう少し詳しく学びたい場合はこちらもどうぞ。筆者もかなり参考にさせてもらっています!

シェアする
ひびきをフォローする
Hbk project