2014/12/30

ライブラリ:nose

Pythonの「 nose 」というライブラリをご紹介します。

import nose

nose はユニットテスト用のライブラリです。「 unittest 」という名前そのままのユニットテスト用ライブラリが標準ライブラリに含まれてはいますが、これはあくまでも xUnit 系列の標準ライブラリ、的な位置づけのもの。nose を使えば、ユニットテストをよりかんたん・シンプルに活用することができます。

以下、 nose の基本的な使い方を見ていきます。まずは最小単位から。


テストケースの書き方

次のようなスクリプトを作ります。
# test_int.py
# assert 文をシンプルに書ける ok_ eq_ メソッドの読み込み
from nose.tools import ok_, eq_

# テストケース
# test で始まる名前の関数で書けばOK
def test_1_is_true():
    ok_(bool(1))

def test_1_plus_2():
    eq(1 + 2, 3)

ここでは、ライブラリを読み込んで関数を2つ定義する、というごくごくシンプルなテストスクリプトを書いています。名前が test から始まるこれらの関数が nose ではテストケースとして認識されます。

ok_ eq_ は assert 文をよりシンプルに書くためのラッパ関数です。これを使うとよりシンプルに書くことができますが、必須ではありません。通常の assert を使っても大丈夫です。


テストの実行の仕方

テストの実行は nosetests コマンドで行います。

上記スクリプトがあるディレクトリに移動したら nosetests コマンドを実行します。
$ nosetests test_unit.py

するとテストが実行され、以下のようなメッセージが表示されます。
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

ここでは . がひとつのテストケースを表します。具体的にどのテストケースが実行されたのか確認したい場合は詳細表示のための -v オプションを利用します。
$ nosetests test_unit.py -v

テスト名が表示されるようになりました。
test_int.test_1_is_true ... ok
test_int.test_1_plus_2 ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK


テストの自動探索と実行

このようにテストスクリプトを直接指定するやり方でもOKですが、引数なしの nosetests コマンドを使うと、もっとかんたんにテストを実行することができます。
$ nosetests

nosetests が実行されると、 test で始まるスクリプトの中から test で始まる関数が探索され、順次実行されていきます。


インストール

pip が入っていれば pip install でインストールすることができます。
$ pip install nose


以上です。

ここでご紹介したのは nose の機能のごく一部です。ほかにも、 unittest と同じようにクラスを使ってテストを書く方法や unittest や doctest も合わせて実行する機能、 setup/teardown 機能など、便利な機能が豊富に用意されています。

nosetest についてはわかりやすい説明をされている方がすでにたくさんいますので、詳しく知りたい方は参考ページをご覧になってみてください。


参考
nose まとめ。とてもわかりやすいです。日本語で nose について学ぶならベストかと思います。
Nose 利用ノート

以下英語です。
「 nose って何がいいの?」という質問への答えが書かれたスレッドです。
http://stackoverflow.com/questions/5696884/python-nose-vs-unittest

nose の基礎から少し踏み込んだ内容まで1ページでまとめて説明されています。
nose introduction - Python Testing

こちらは公式ドキュメント。「基本的な使い方」のページです。
Basic usage — nose公式ドキュメント

こちらのページもシンプルでわかりやすいです。カバレッジモジュールについても触れられています。
miscellaneous

事例ベースで node を学ぼうという趣旨のページ。実コードが見たい、という方に。
Python testing with nose by example | Cesar's code

2014/12/27

Python Tips:IPython 起動時の readline 関連の警告をなくしたい

環境によりますが、MacOS X で IPython を pip からインストールして起動すると、起動時に次のような文章を含むメッセージが出ることがあります。

libedit detected - readline will not be well behaved, including but not limited to:
   * crashes on tab completion
   * incorrect history navigation
   * corrupting long-lines
   * failure to wrap or indent lines properly
It is highly recommended that you install readline, which is easy_installable:
     easy_install readline
Note that `pip install readline` generally DOES NOT WORK, because
it installs to site-packages, which come *after* lib-dynload in sys.path,
where readline is located.  It must be `easy_install readline`, or to a custom
location on your PYTHONPATH (even --user comes after lib-dyload).

この場合、 readline というPythonライブラリが不足しているということなので、 readline を入れれば解決します。

ただし、警告文で指示されているとおりに pip でインストールするとうまく解消できないことがあります。
$ pip install readline 

この場合は readline をいったんアンインストールしてから easy_install で入れるとうまくいくことがあるようです。
$ pip uninstall readline
$ easy_install readline

以上です。


参考
pip installing ipython with readline on OSX Lion - Stack Overflow
python - ipython complaining about readline - Stack Overflow

2014/12/23

Pythonのデコレータの使い方

Pythonのデコレータの使い方について見てみます。

デコレータとは、既存の関数やクラスを「装飾」する機能のこと。デコレータを使うことで、既存の関数やクラスの中身を直接触ることなく、その外側から機能を追加したり書き換えたりすることができます。

Pythonでは、関数やクラスもオブジェクトということもあって、自分オリジナルのデコレータをかんたんに作ることができます。

具体的に見ていきます。次のような hello 関数が定義されているものとします。
# あいさつを返す関数
def hello():
    return "konichiwa"

print hello()  # => konichiwa
# 定義したとおりの結果が返ってくる

続いて、 mydec 関数を定義し hello 関数を書き換えます。
# デコレータパターンを使うために
# 関数を受け取り関数を返す関数を定義する
def mydec(func):
    def new_func():
        print "%s function called".format(func.__name__)
        return func()
    return new_func

# hello を書き換え
hello = mydec(hello)

print hello()
# => hello function called
#    konichiwa
書き換えた結果、新たに作られた hello は元の hello と同じ戻り値を保ちながら、標準出力に hello function called と出力するものに変わりました。

すでにある関数を装飾するこの一連の処理がデコレータパターンです。キモとなっているのは hello = mydec(hello) の一文です。

Pythonではこの一文は @ を使ったシンタックスシュガーでシンプルな形に書き換えることができます。これがいわゆるデコレータ構文です。
@mydec
def hello():
    return "konichiwa"
このコードは次のコードと等価です。
def hello():
    return "konichiwa"
hello = mydec(hello)

「@デコレータ名」をいくつも積み重ねて、複数のデコレーションを行うことも可能です。


上記の hello は引数がない場合でしたが、引数がある関数に適用できるデコレータも、可変長引数などを使ってかんたんに定義ことができます。
def mydec(func):
    def new_func(*args, **kwds):
        print “%s function called”.format(func.__name__)
        return func(*args, **kwds)
    return new_func

@mydec
def some_function(*args):
    # ... content

ただし、このやり方では、関数のアトリビュート __name__ や __doc__ が書き換えられてしまうという問題があります。

この書き換え問題を手軽に解決してくれるのが、標準ライブラリの functools.wraps です。この wraps デコレータを使えば、書き換え前の関数のアトリビュートを書き換え後のものに手軽に移すことができます。
from functools import wraps

def memoize(func):
    cache = {}
    # wraps を使って元の関数のアトリビュート等を書き換え後の関数にコピーする
    @wraps(func)
    def decorated_func(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return decorated_func

@memoize
def some_function(*args):
    # ... content

一見ややこしいのですが、 wraps は次のような形で使うデコレータです。
@wraps(元の関数)
def 書き換え後の新しい関数():
    # 新しい関数の中身
よくよく考えればこの書き方になるのも納得ですが、このあたりはもう定型パターンということで、難しく考えずに「こう書くもの」と捉えてしまってもよいかもしれません。


以上です。


ちなみに、@構文は関数だけでなくクラスに対しても適用することができます。また、デレコータそのものも関数ではなくクラスとして定義することなんかも可能です。

もっと詳しく見てみたい場合は、わかりやすい解説をしている方がたくさんいらっしゃるので参考ページを参照してみてください。

参考
英語ですがとってもわかりやすいです。
Decorators and Functional Python

いろんなデコレータパターンを紹介してあります。
PythonDecoratorLibrary - Python Wiki

日本語で書かれている解説ではこのあたりのページがおすすめです。
Python - デコレータ
Pythonのデコレータを理解するときに残したメモ - kk6のメモ帳*

公式ドキュメントの解説。ちょっと短すぎるような。。。
functools.wraps — Python公式ドキュメント

2014/12/16

Python Tips:特異メソッドを作りたい

Pythonで「特異メソッド」を作る方法をご紹介します。

特異メソッドというのは(私が知るかぎり)Ruby発祥のことばで、「特定のオブジェクトだけが持つメソッド」のこと。インスタンスメソッドは特定のクラスのインスタンスであればどのインスタンスからも呼び出すことができますが、特異メソッドはある特定のインスタンスからしか呼び出すことができません。

types ライブラリの MethodType というコンストラクタを使えばPythonでも特異メソッドを作ることができます。

具体的に見ていきます。
# ライブラリの読み込み
from types import MethodType

class Dog(object):
    def __init__(self, name):
        self.name = name

d1 = Dog(“inu”)

# 以下で特異メソッドを追加していきます
# まずは特異メソッドにしたい処理を関数として用意
def hello(self):
    print “%s: bow!” % self.name

# 特異メソッドとしてオブジェクトのアトリビュートに追加
# format: MethodType(method, obj)
d1.hello = MethodType(hello, d1)

d1.hello()  # => inu: bow!
# d1 をレシーバに hello メソッドが呼び出せるようになる

d1 だけで使えるインスタンスメソッドを追加することができました。

hello は d1 だけに追加したアトリビュートなので、他のインスタンスで利用することはできません。
d2 = Dog(“hayato”)
d2.hello()  # => AttributeError
# hello はあくまでも d1 のメソッドなので他のインスタンスからは呼び出せない


ちなみに、MethodType を使わず
d1.hello = hello
とやっても一見追加できるように思いますが、これだとただの関数となってしまい、関数内でレシーバである d1 への参照を持つことができません。

また、次のようにやってもうまく行きません。
d1.__class__.hello = hello
これだと hello はクラス全体で共有されるインスタンスメソッドになるため、こちらの方法も特異メソッドを作るには不適切です。

以上です。


ちなみに、次の部分はハードコーディングしてしまっているので、再利用性が高くありません。
d1.hello = MethodType(hello, d1)

他でも使うことを考えてもっときれいに書くなら次のような感じになるでしょうか。
def add_eigen_method(obj, method):
    setattr(obj, method.__name__, MethodType(method, obj)

add_eigen_method(d1, hello)


Rubyほどシンプルにきれいに書くことはできませんが、Pythonでも特異メソッドを利用することはできます、というお話でした。


参考
python - Adding a Method to an Existing Object - Stack Overflow
Hoarded Homely Hints: Python Metaprogramming: Dynamically Adding Methods to Classes
Dynamically Adding a Method to Classes or Class Instances in Python - Ian Lewis

8.15. types — Names for built-in types — Python公式ドキュメント

2014/12/11

サンプルコード:ファイルの中身を行単位でソートして出力

サンプルコード:ファイルの中身を行単位でソートして出力


Python でファイルの中身を行単位でソートし標準出力へと出力するサンプルコードをご紹介します。

# coding: utf-8
import sys

# コマンドライン引数を取得
argv = sys.argv

if len(argv) < 2:
    print("usage: python this_script.py target_filename")
    exit()

# コマンドライン引数の最後に与えられたファイルを読み込んで行をソートし出力する
with open(argv[-1]) as f:
    lines = f.readlines()
    lines_sorted = sorted(lines)
    for l in lines_sorted:
        print(l.rstrip("\n"))

readlines() sorted() 、 rstrip() あたりを使うとシンプルに記述することができます。

2014/12/10

Python Tips:順列や組み合わせを作りたい

Pythonで、数学でいう nPr や nCr 、いわゆる「順列」や「組み合わせ」を作る方法をご紹介します。

結論としては itertools ライブラリを使う方法がかんたんです。

以下、順番に見ていきます。
  1. 順列
  2. 組み合わせ


順列

itertools.permutations を使います。
from itertools import permutations

original = "abc"
permutations(original)
# => [('a', 'b', 'c'),
      ('a', 'c', 'b'),
      ('b', 'a', 'c'),
      ('b', 'c', 'a'),
      ('c', 'a', 'b'),
      ('c', 'b', 'a')]
引数には、要素を格納したリストなどを渡します。

int を第2引数として与えると長さを指定することもできます。
permutations(original, 2)
# => [('a', 'b'), 
      ('a', 'c'), 
      ('b', 'a'), 
      ('b', 'c'), 
      ('c', 'a'), 
      ('c', 'b')]

順列は英語で permutations なので、名前そのままですね。


組み合わせ

こちらは itertools.combinations を使います。

こちらも組み合わせの英語にあたる「combination」と、そのままの名前になっています。
from itertools import combinations

original = "abc"
combinations(original, 2)
# => [('a', 'b'), ('a', 'c'), ('b', 'c')]
こちらも、第1引数にリストなどの iterable な要素を、第2引数に int を渡します。

こちらは permutations と異なり、第2引数が必須となっています。


以上です。


参考
9.7. itertools — Functions creating iterators for efficient looping — Python公式ドキュメント
algorithm - How to generate all permutations of a list in Python - Stack Overflow
Python code to pick out all possible combinations from a list? - Stack Overflow

2014/12/03

Python Tips:画像の Exif データを取得したい

Pythonを使って、画像の Exif データを取得する方法をご紹介します。

Exif データとは、画像の各種メタ情報を表すためのフォーマットのことです。写真であれば、カメラの機種や撮影した日付、位置情報などのデータが格納されています。

必ずしもすべての画像に備わっているわけではないので、「ついてる画像にはついてる」といった位置づけの情報です。

ちなみに Exif は Exchangeable Image File Format の略だとか。


この Exif データをPythonを使って取得する方法を以下ご紹介します。

結論からいえば、 PIL ライブラリを使う方法がかんたんです。

コードを見てみます。
# ライブラリ読み込み
from PIL import Image
from PIL.ExifTags import TAGS


# 関数の定義 01
def get_exif_of_image(file):
    """Get EXIF of an image if exists.

    指定した画像のEXIFデータを取り出す関数
    @return exif_table Exif データを格納した辞書
    """
    im = Image.open(file)

    # Exif データを取得
    # 存在しなければそのまま終了 空の辞書を返す
    try:
        exif = im._getexif()
    except AttributeError:
        return {}

    # タグIDそのままでは人が読めないのでデコードして
    # テーブルに格納する
    exif_table = {}
    for tag_id, value in exif.items():
        tag = TAGS.get(tag_id, tag_id)
        exif_table[tag] = value

    return exif_table

print get_exif_of_image("sample.jpg")
# => Exif 情報を格納した辞書
#    Exif 情報がない場合には空の辞書

まずは、PIL ライブラリから画像ファイルを開くための Image クラスと Exif 情報を読み取るための TAGS という辞書を読み込みます。

次に、これらを使って実際に画像を読み込み Exif データを取得します。 PIL を使えば、 Exif データは _getexif() メソッドで取得することができます。

get_exif_of_image() に画像ファイル名を渡すと、その情報を返してくれるはずです。


次に、Exif データのうち、日付情報だけを取り出す方法を見てみます。
# 関数の定義 02
def get_date_of_image(file):
    """Get date date of an image if exists

    指定した画像の Exif データのうち日付データを取り出す関数
    @return yyyy:mm:dd HH:MM:SS 形式の文字列
    """

    # get_exif_of_imageの戻り値のうち
    # 日付データのみを取得して返す
    exif_table = get_exif_of_image(file)
    return exif_table.get("DateTimeOriginal")

print get_date_of_image("sample.jpg")
# => yyyy:mm:dd HH:MM:DD 形式の文字列
#    Exif データが存在しない場合は None

上の get_exif_of_image 関数の方で例外処理なんかはしてあるので、こちらの関数ではその戻り値から日付情報を抜き出すだけです。

PIL の場合、日付情報は DateTimeOriginal という名前で扱われるので、これをキーとしてそのまま指定しています。


以上です。

使いどころとしては、画像ファイルの名前のフォーマットを統一したい場合などでしょうか。カメラを複数台使っている場合なんかには、画像ファイルの名前が機種ごとにまちまちだったりするので、それを統一するときなんかには便利だと思います。

あとは、カメラの使用日時を統計データとしてまとめたいとき、なんかでしょうか。


参考
Extracting image EXIF data with Python | Endlessly Curious
In Python, how do I read the exif data for an image? - Stack Overflow
How do I use Python's PIL library to get EXIF data from a photo starting with a hyperlink to the photo? - Stack Overflow

Exchangeable image file format - Wikipedia