2013/11/30

Python Tips:リストから重複した要素を削除したい

Python で、リストから重複した要素を取り除き、ユニークな要素だけのリストを得る方法をご紹介します。

早速ですが、いちばんカンタンで Python らしい書き方は set を使うパターンでしょうか。
li = [3, 4, 3, 2, 5, 4]
li_uniq = list(set(li))  # [2, 3, 4, 5]
いったん set にしてからリストに戻すことで重複要素が自動的に削除されます。

ただしこの方法だと要素の順番が変わることもあるようです。要素の順番を保ちたい場合には、普通に for ループを回す形になります。
li = [3, 4, 3, 2, 5, 4]
li_uniq = []
for x in li:
    if x not in li_uniq:
        li_uniq.append(x)
li  # => [3, 4, 2, 5]

この「リストから重複を取り除く方法」について、 StackOverflow ではリスト内包表記を使ったアプローチや高速化する方法等が議論されています。たとえば、内包表記を使ってかつ高速化するひとつのアプローチとして、次のようなやり方が紹介されています。
def f7(seq):
    seen = set()
    seen_add = seen.add
    return [ x for x in seq if x not in seen and not seen_add(x)]

seen_add = seen.add あたりはオブジェクトのアトリビュート参照の時間も削減しようという試みでしょうか。要素の数が膨大になるような場合には効果を発揮するかもしれません。


参考

2013/11/26

Python Tips:Sublime Text 2でvirturalenv環境のPythonを使いたい

Sublime Text 2でPythonのコードを書いていると、ショートカット(Ctrl+b や Command+b)で走るPythonを指定したいことがあります。

今回はその例として、virtualenv環境のPythonを使う方法をご紹介します。

1 Python.sublime-build の設定
{
  // "cmd": ["python", "-u", "$file"],
  "shell": true,
  "cmd": ["path/to/virtualenv/bin/activate && python -u \"$file\""],
  "file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
  "selector": "source.python"
}
デフォルトの設定は、コメントアウトしてある最初の「cmd」の行です。それをコメントアウトし、新たに「shell」というプロパティを追加、「cmd」プロパティを再設定します。

「&& python」の前の「path/to/virtualenv/bin/activate」の部分にvirtualenv環境を読み込むコマンドを記述します。ちなみに、私の環境ではbashシェルを使っていて、.bashrcの中でvirtualenv環境を有効にしているので、この部分には単純に「source ~/.bashrc」を入れています。

ポイントは、デフォルトではcmdの値は複数の要素を持つ配列になっているのですが、変更後はひとつの文字列だけを持つ配列に収める、というところです。他のシェルは試していませんが、bashの場合は複数の要素を持つ配列にするとうまく動いてくれません。


参考
Build Systems - Sublime Text Help
Build Systems - Sublime Text Unofficial Documentation

2013/11/19

Python Tips:ターミナルのコマンドを利用したい

Python上でターミナル(コマンドライン)のコマンドを利用できる機能についてご紹介します。

import subprocess 

subprocess.call(cmd)
subprocess.check_call(cmd)
subprocess.check_output(cmd)

コマンド関連の機能はsubprocessの中にまとめられています。代表的なものはcall、check_call、check_outputの3つでしょうか。

subprocess.call('ls')
# ただ実行するだけ 
# 結果ステータスを返す
# 0なら成功
subprocess.check_call('ls')
# call とほとんど同じだが
# エラーなら CalledProcessError 例外をあげてくれる
subprocess.check_output('ls')
# check_call とほとんど同じだが
# コマンドの標準出力を返す

callがいちばんシンプルで、check_call、check_outputになると機能が豊富になっていきます。callは実行してステータスを返すだけ、check_outputは出力を文字列で取得できエラーの例外処理もPython上で行うことができます。

例外処理や出力の再利用のことなんかを考えると、通常はcheck_outputを使うのが良さそうです。

コマンド関連の機能がsubprocessモジュールにまとめられる以前は、osモジュール内のos.systemやcommands内のgetstatusoutputなどがありましたが、現在では推奨されていないようです。

import os
os.system('ls')
# ステータスを返す
# subprocess.call('ls') に近いもの
import commands
commands.getstatusoutput('ls')
# ステータスと出力をリストにして返す
# subprocess.check_output('ls') に近いもの

その他、サブプロセスをもっと細かく扱いたい場合には、Python 3の場合はsubprocess.Popenクラス、Python 2の場合はpopen2モジュールを使うのがよいようです。


参考
subprocess - Python公式ドキュメント
os - Python公式ドキュメント
commands - Python公式ドキュメント
Python pythonからシェルコマンドを叩く - かぴぶろぐ