2014/03/26

Python Tips:辞書のキーとバリューを逆にしたい

Pythonで、辞書型のオブジェクトのキーとバリューを入れ替える方法についてご紹介します。

最もかんたんなやり方は、辞書内包表記を使う方法です。
mydict = {"a": "amembo", "i": "inu", "u": "usagi"}

mydict_inv = {v:k for k, v in mydict.items()}
# => {'amembo': 'a', 'inu': 'i', 'usagi': 'u'}

ちなみに Python 2.6 以前にはこの辞書内包表記がなかったとのことなので、2.6以前でやるとしたら次のやり方になるでしょうか。
mydict_inv = dict([(v, k) for k, v in mydict.items()])


参考
dictionary - Python reverse / inverse a mapping - Stack Overflow

2014/03/17

Python Tips:リストから辞書を作成したい

Pythonでリストから辞書を作成する方法についてご紹介します。

まずは、元のリストが要素をふたつずつ持ったタプルからできている場合。この場合はそのまま dict 関数を使います。
# リストの要素がすでに2個組のタプルとなっている
li1 = [("a", 3), ("b", 2), ("c", 5)]

di1 = dict(li1)
# => {'b': 2, 'c': 5, 'a': 3}

辞書の内包表記を使えば、次のような書き方をすることもできます。
d = {k: v for (k, v) in li1}
# => {'b': 2, 'c': 5, 'a': 3}

次に、元のリストが「キー バリュー キー バリュー ...」というように、キーとバリューを交互に格納したリストの場合。この場合は、いったんキーとバリューがセットになったタプルを作ってから dict 関数に渡す形だときれいに書けます。
# リストの要素が交互に並んだキーとバリューになっている
li2 = ["a", 3, "b", 2, "c", 5]
di2 = dict(zip(li2[0::2], li2[1::2]))
# => {'b': 2, 'c': 5, 'a': 3}

ここで、 li2[0::2] は奇数番目の要素、 li2[1::2] は偶数番目の要素を収めたリストとなっているので、これらを zip 関数に渡すことで奇数番目と空数番目の要素をセットにしたタプルのリストができあがります(正確にはリストではなくイテレータができあがります)。


参考
Python: create a dictionary with list comprehension - Stack Overflow
Python Ordered Dictionary to regular Dictionary - Stack Overflow

2014/03/12

ライブラリ:unittest

PythonのJUnit系のテストライブラリ unittest についてご紹介します。


使い方

まずは最もベーシックな使い方から。

最小の流れは次のようなものになります。
unittest ライブラリの読み込み
unittest.TestCase を継承したクラスの中にテストケースを書く
unittest.main() でテストを実行する

ことばで聞くよりコードを見るのが早いので、例を見てみます。
# unittest ライブラリを読み込んでおく
import unittest


# テスト対象の関数
def factorial(n):
    """factorial function"""
    if n < 2:
        return 1
    return factorial(n - 1)


# テストケースをまとめたクラス
# メソッド名が test で始まるメソッドがひとつのテストとなる
class FactorialTest(unittest.TestCase):

    def test_factorial_with_arg_1(self):
        expected = 1
        actual = factorial(1)
        self.assertEqual(expected, actual)


# 直接実行されたときのみ unittest.main() を呼び出して
# テストケースを回す
if __name__ == "__main__":
    unittest.main()
ここで、 factorial の部分はテストではなくテスト対象となるコードです。簡単化のために、テスト対象コードとテストケースとが同一モジュールに入っている状態にしています。

unittest.main() が呼び出されると、そのスクリプトの中で unittest.TestCase を継承したすべてのクラスがテストケースのかたまりとして認識され、そのメソッドのうち名前の先頭が test で始まるメソッドだけがテストケースとして実行されます。

各テストケースの中にはひとつ以上のアサート用メソッドを書きます。 unittest のアサート用のメソッドは「self.assert***」という形で呼び出します。***の部分には Equal True False などさまざまなアサーションチェックが行える単語が入ります。

具体的なアサート用のメソッドとしては、次のようなものが用意されています。

  • assertEqual(a, b)
  • assertNotEqual(a, b)
  • assertTrue(x)
  • assertFalse(x)
  • assertIs(a, b)
  • assertIsNot(a, b)
  • assertIsNone(x)
  • assertIsNotNone(x)
  • assertIn(a, b)
  • assertNotIn(a, b)
  • assertIsInstance(a, b)
  • assertNotIsInstance(a, b)


このスクリプトを python スクリプト名 で実行すると、テストが実行され、OKやエラーなどの結果が表示されます。

unittest の最小単位はこれだけです。

各テストケースの前後でお決まりの処理を行いたい場合は setUp tearDown メソッドを使います。
class FactorialTest(unittest.TestCase):
    # 各ケースの最初に実行してほしい処理
    def setUp(self):
        self.f = open(targetfile, 'r')

    # 各ケースの終わりに実行してほしい処理
    def tearDown(self):
        self.f.close()
setUp には最初の処理を、tearDown には最後の処理を書いておきます。 python スクリプト名 とするだけでは、アサーションに失敗しないかぎり、具体的にどのテストケースが実行されているのかの詳細は表示されません。テストの詳細を表示してほしい場合はpythonコマンドに -v オプションを渡します。
$ python スクリプト名 -v

スクリプトの中にあるテストケースすべてではなく、特定のテストケース(クラス)だけを指定して実行したい場合は -m オプションで unitest を読み込んだあとにクラス名で指定します。
$ python -m unittest スクリプト名.テストケース名

たとえば、上記の FactorialTest が factorial.py というファイルに入っている場合であれば次のように実行します。
$ python -m unittest factorial.FactorialTest

テストスクリプトを個別に指定せずに、まとめて実行してほしい場合には unittest の discover サブコマンドを使います。
$ python -m unittest discover

discover について詳しくは公式ドキュメントの discover の項をご覧ください。


インストール

unittest はデフォルトでPythonの実行環境に含まれているため、別途インストールは不要です。

import unittest とすることですぐに使い始めることができます。


以上です。


参考
unittest — Unit testing framework — Python公式ドキュメント
Test-Driven Development in Python - O'Reilly Media
Testing Your Code — The Hitchhiker's Guide to Python

2014/03/03

Python Tips:ライブラリをまとめてインストールしたい

Pythonのライブラリをまとめてインストールする方法をご紹介します。

pip がインストールされていれば、 pip install の -r オプションで一括インストールが可能です。
$ pip install -r requirements.txt
$ pip install --require requirements.txt  # このように書いても OK

requirements.txt の中身は次のような形でライブラリの一覧を書きます。
cssselect==0.9.1
lxml==3.2.4
mechanize==0.2.5
pyquery==1.2.6
requests==2.0.1
selenium==2.37.2
wsgiref==0.1.2

この形は pip freeze の出力形式と同じなので、特定のライブラリ環境を別のところに移したい場合なんかにも pip freeze 、 pip install でかんたんに移すことが可能です。

$ # コピー元の環境でライブラリ一覧を作成
$ pip freeze > requirements.txt
$ 
$ # 新しい環境に移ってまとめてインストール
$ pip install -r requirements.txt


以上です。

ちなみに virtualenvwrapper で環境を新規作成する際にも同様のオプションを取ることができます。
$ mkvirtualenv 環境名 -r requirements.txt

こうすれば、新しい環境を生成した直後に特定のライブラリをインストールしてくれます。

ちなみに、ライブラリ指定に加えて「使用するPythonの指定をしたい」といった場合には
$ mkvirtualenv 環境名 --python=/path/to/specific/python -r requirements.txt
とすれば大丈夫です。


参考
Usage — pip 公式ドキュメント