静かなる名辞

pythonとプログラミングのこと


【python】文字列を一文字ずつのリストにする

 文字列はそもそもiterableなので、これが必要なことは滅多にないんだけど・・・。

>>> for c in "hogehoge": # そのままfor文に渡してオッケー。
...     print(c)
... 
h
o
g
e
h
o
g
e

 この前必要になったので(ライブラリの引数でlist型を要求された)考えてみる。

 目次

スポンサーリンク



考えられる方法

空リストを用意してfor文で一文字ずつappend

 さすがに面倒くさいので論外。

リスト内包表記

 pythonは何でもリスト内包表記で書ける。よってこの処理もリスト内包表記で書ける。

>>> [c  for c in "hogehoge"]
['h', 'o', 'g', 'e', 'h', 'o', 'g', 'e']

 メリットは、

  • 他の方法と比べると可読性(不可誤読性)と簡潔さのバランスが良い

 デメリットは、

  • 冗長
  • ぶっちゃけforループなので遅い

 ぶっちゃけ長いので、積極的に使いたくはない。

list(str)する

 list(str)で一文字ずつのリストになる。

>>> list("hogehoge")
['h', 'o', 'g', 'e', 'h', 'o', 'g', 'e']

 メリットは、

  • 簡潔
  • 組み込みなので速いと思われる

 デメリットは、

  • 誤読されるかも?

 誤読されるというのは難癖に近いが、

>>> list("hogehoge") # -> 他人に["hogehoge"]だと解釈される可能性がある!

 ということ。ないとは言い切れない。

starred expression

 こんな書き方もできる。

>>> [*"hogehoge"]
['h', 'o', 'g', 'e', 'h', 'o', 'g', 'e']

 メリットは、

  • 恐らく一番簡潔

 たった3文字の追加でリストにできる! list()だと6文字なので半分の記述量で済む

  • 見た目がカッコいい

 デメリットは、

  • 古いバージョンだと確か使えない
  • 可読性は良くないかも。というか始めてみた人は面食らう気がする
  • 速度性能が未知数

 しょうじき見た目は一番好きなので積極的に使っていきたいのだが、遅いと困る・・・。

時間を計測してみた

 計測しました。

# coding: UTF-8

import time

def time_measure(string, splitter, n=1000):
    time_list = []
    for i in range(n):
        t1 = time.time()
        splitter(string)
        t2 = time.time()
        time_list.append(t2-t1)
    return sum(time_list)/n

def main():
    string = "hoge"*100000
    
    lm = lambda s:None
    f1 = lambda s:[c for c in s]
    f2 = lambda s:list(s)
    f3 = lambda s:[*s]

    # 念の為空のラムダも回す
    print(time_measure(string, lm))
    print(time_measure(string, f1))
    print(time_measure(string, f2))
    print(time_measure(string, f3))

if __name__ == "__main__":
    main()

 結果は、

1.652240753173828e-07
0.008317208766937256
0.00259556245803833
0.0026724901199340822

 意外なことに、list(str)と[*str]は互角だった(プログラム走らせる度に誤差で優劣が変わる程度)。

 個人的には古いバージョンのpythonは使わないし、可読性もどっこいどっこいだと思うので、今後は[*str]で行くことにしました。

 追記:上のように思っていた時期もありました。でもやっぱりトリッキーな感じがするので、たいたいlist(str)ばっかり使っています。この方が白python的な気もするし……。[*str]は黒python寄り。