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 あたりはオブジェクトのアトリビュート参照の時間も削減しよう、という試みでしょうか。


参考
Get unique values from a list in python - StackOverflow
In Python, what is the fastest algorithm for removing duplicates from a list so that all elements are unique *while preserving order*? - StackOverflow
How do you remove duplicates from a list in Python whilst preserving order? - StackOverflow

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からシェルコマンドを叩く - かぴぶろぐ