2013/04/29

ライブラリ:shelve

Pythonの「shelve」というライブラリについてご紹介します。

import shelve

「shelve」は、Pythonオブジェクトをファイルに保存し永続化するためのライブラリです。簡易的なデータベースとして使うことができます。インタフェースが辞書型データと非常によく似ているため、シンプルに使うことができます。

ただ、個人的にはほとんど使ったことがないため細かいところがまだよくわかっていません。以下、今回調べた基本的な使い方だけ見てみます。

オブジェクトデータの保存

FILENAME = 'my.shelve'

d = shelve.open(FILENAME)
d['dogs'] = ['Taro']
d.close()
オブジェクトの保存は通常のファイルへの保存と同じように、open()とclose()の間に処理を書く形で行います。

openして得たshelveオブジェクトに、「キー&バリュー」の形でデータを与えます。ファイルが存在しない場合は新規に作成してくれます。

オブジェクトデータの読み込み

d = shelve.open(FILENAME)
print d  # {'dogs': ['Taro']}
print d['dogs']  # ['Taro']
print 'dogs' in d  # True
d.close()
shelveで保存したデータはopen()で読むことができます。shelveオブジェクトは辞書型のデータと同じように扱うことができます。

オブジェクトデータの追加

d = shelve.open(FILENAME)
dogs = d['dogs']
dogs.append('Pochi')
dogs.append('Jiro')
d['dogs'] = dogs
d.close()

d = shelve.open(FILENAME)
print d  # {'dogs': ['Taro', 'Pochi', 'Jiro']}
d.close()
バリューの部分をリストにした場合などにそのリストに要素を追加する場合には、いったんそのリストを取り出し、値を追加した後に元に戻します。

・・・以上です。


インストール
「shelve」は標準ライブラリに含まれているので、Pythonと別途インストールする必要はありません。


参考
shelve Python Module of the Week
shelve - Python公式ドキュメント

2013/04/22

ライブラリ:cmath

Pythonの「cmath」というライブラリについてご紹介します。

import cmath

「cmath」は、実数だけでなく「虚数」も含んだ、いわゆる「複素数」を扱うためのライブラリです。

Pythonのデータ型のところでも挙げましたが、Pythonには複素数を扱える数値型が用意されています。たとえば、実数部が3、虚数部が4の数値は虚数単位 j を使って次のように表すことができます。
x = 3 + 4j

虚数同士の演算も実数と同じように行えます。
x + x  # 6 + 8j
x * x  # -7 + 24j

実数部、虚数部を取り出すには x.real と x.imag を使います。
x.real  # 3.0
x.imag  # 4.0

cmathは、この複素数型の数値に対して使える基本的な関数を用意しています。以下、使う頻度が比較的高いかなと思える関数をピックアップしてご紹介します。

cmath.polar(x)  # (5.0, 0.927...)
cmath.phase(x)  # 0.927...
複素数といえば極座標、かと思うのですが、polar()とphase()は、xy座標から極座標へと座標変換を行ってくれる関数です。polar(x)は複素数xに対して、(半径r, 位相θ)というペアを返します。一方のpolar(x)は、位相のみを返します。

cmath.rect(5.1, 0.9273)  # (3.0..., 3.99...)
rect()は、polar()やphase()とは逆に、極座標からxy座標に変換する関数です。与えられた(r, θ)のペアに対して、(x, y)のペアを返します。

cmath.exp(x)
exp(x)は、複素数の範囲で指数e**xを計算する関数です。mathライブラリのexp()の場合は実数の範囲内でのみ計算を行いましたが、cmathのexp()は虚数も含めた範囲で計算を行います。

・・・以上です。


インストール
「cmath」は標準ライブラリに含まれているので、Pythonと別途インストールする必要はありません。


参考
cmath - Python公式ドキュメント

2013/04/17

ライブラリ:math

Pythonの「math」というライブラリについてご紹介します。

import math

「math」ライブラリには、数学にまつわる変数と関数が入っています。

目的によって使う関数は異なってくるかとは思いますが、mathライブラリに含まれている変数・関数のうち一般によく使われそうなものを以下にピックアップしてご紹介します。

math.pi  # πを返します 3.1415... 
math.e  # eを返します  2.718...
piとeはその名のとおり、数学でよく使う定数、円周率「π」と自然対数の底「e」(の近似値)とを返します。math内の変数はこのふたつのみです。

math.floor(5.2)  # 5を返す
math.ceil(5.2)  # 6を返す
floor()とceil()は、浮動小数点型の数値を整数に変換する関数です。floor(x)はその名のとおり下側、x以下の最大の整数を返します。一方ceil()は、x以上の最小の整数を返します。

math.floor(-5.2)  # -6を返す
math.ceil(-5.2)  # -5を返す
マイナスの数字を扱うときには、floor(x)は絶対値が大きくなる方向に、ceil(x)は逆に絶対値が小さくなる方向に丸められる点に注意が必要です。

math.trunc(5.2)  # 5を返す
math.trunc(-5.2)  # -5を返す
trunc()は、floor()、ceil()と同じく整数を返す関数ですが、こちらは、小数点以下の切り捨てを行います。ですので、引数が正の数でも負の数でも共通して0方向へと丸めてくれます。

math.exp(5)
math.log(5)
math.pow(5, 2)  # 25.0
math.sqrt(5)  # 2.236...
exp(x)、log(x)、pow(x, y)、sqrt(x)も名前のとおりで、それぞれ、e^x、log(x)、x^y、x^(1/2)を返してくれる関数です。

math.sin(1)
math.cos(1)
math.tan(1)
三角関数も用意されています。sin(x)はサイン、cos(x)はコサイン、tan(x)はタンジェントを返してくれます。いずれも引数の単位は度ではなく「ラジアン」です。三角関数についてはこれら以外にも、サインやコサインの逆となるいわゆるアークサインやアークコサイン、ハイパボリックサインなんかも用意されています。

三角関数周りの数字を扱うときには、内部での計算はラジアンで、表示するときは度で、と単位を変えることがよくあるかと思うのですが、そんなときには次の2つの関数が使えます。
math.degrees(1)
math.radians(30)
degrees()はラジアンから度に、radians()はその逆、度からラジアンに単位を変換してくれます。

・・・以上です。


ちなみに、数値の符号(正か負か)を返す関数は、mathライブラリの関数としてではなく、
sign(-5)  # -1
と単体で使えるようになっています。

複素数に関する関数については、別の「cmath」というライブラリにまとめられています。大学などの研究で使う計算を行うときには、「Numpy」「SciPy」という科学技術計算用のライブラリが便利です。


インストール
「math」は標準ライブラリに含まれているので、Pythonと別途インストールする必要はありません。


参考
math - Python公式ドキュメント

2013/04/11

ライブラリ:string

Pythonの「string」というライブラリについてご紹介します。

import string

「string」には、文字列に関係した変数や関数が含まれています。主に
  • 特定のグループの文字列をまとめた変数
  • 文字列をフォーマットする関数
の2つが入っています。

以下、そのそれぞれについて順番に見ていきます。

特定のグループの文字列をまとめた変数

アルファベットなどの文字グループをまとめた変数がいくつか用意されています。ある変数がアルファベットかどうかをチェックしたりするときに使えます。

string.ascii_lowercase
# 'abcdefghijklmnopqrstuvwxyz'が入っている
ascii_lowercaseには、アルファベットの小文字すべてが入っています。「ascii」がつかない「lowercase」というものもありますが、「lowercase」の方はロケーション(言語環境?)によって異なる場合があるそうです「ascii_lowercase」の方はロケーションに依存しない不変のものとなっています。

string.ascii_uppercase
# 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'が入っている
ascii_uppercaseはアルファベットの大文字を含んでいます。こちらも「ascii」のつかない「uppercase」がありますが、ちがいはlowercaseと同様です。

string.digits
# '0123456789'が入っている
string.digitsには、0から9までの数字が入っています。

string.punctuation
punctuationは、文字と文字の間の句読点、括弧などをまとめたものです。私の環境では「!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~」が入っていました。

string.whitespace
whitespaceは、空白や改行関連の文字をまとめたものです。私の環境では「\t\n\x0b\x0c\r 」が入っていました。

文字列をフォーマットする関数

文字列をフォーマットする関数のうち、Python3で使う頻度が高いのはformat()でしょうか。こちらは文字列のメソッドとして、次のようにして使うようです。
'{0}, {1}, {2}'.format('a', 'b', 'c')
# 'a, b, c'が返される

私はPython3を使っていないため、現状このformatは使ったことがほとんどありません。

その他には、文字列を大文字にしたり小文字にしたり、小技をこなす関数がたくさん用意されています。
string.capitalize(文字列)  # 各単語の先頭を大文字にする
string.lower(文字列)  # すべて小文字に
string.upper(文字列)  # すべて大文字に
string.join(リスト, セパレータ)  # リストをjoin
string.strip(文字列)  # 先頭と末尾の空白を削除
string.replace(文字列, 変更したい文字, 変更後の文字)  # 置換

ただ、これらについてはstr型変数のメソッドとして同じ名前のものが用意されているため、この用途でわざわざstringライブラリを読み込んで使う機会は多くないかと思います。

・・・以上です。


インストール
「string」は標準ライブラリに含まれているので、Pythonと別途インストールする必要はありません。


参考
string - Python公式ドキュメント

2013/04/04

ライブラリ:random

Pythonの「random」というライブラリについてご紹介します。

import random

「random」は、乱数に関係したライブラリです。標準ライブラリのひとつで、Pythonをインストールするとデフォルトで入っています。

0から1の間の浮動少数点数を返すrandom()をベースに、整数を返す関数、与えられた任意のリストの中からひとつを抽出する関数など乱数関連の関数が豊富に用意されています。

以下ではその中からいくつかピックアップしてご紹介します。

random.random()
random()は、0.0から1.0の間の浮動小数点数を返します。厳密には区間[0.0, 1.0)、つまり、0ちょうどを含む「0以上」から1ちょうどを含まない「1未満」までの数を返します。

random.randrange(start, stop)
randrange()は、startからstopまでの整数の中からランダムにひとつを返します。forループでよく使うrange(start, stop)と同じように、startそのものは含みますが、stopそのものは含みません。つまり、「start、start+1、start+2、...、stop-1」の合計「stop-start」個の整数が抽出対象の集合となります。

random.randint(a, b)
こちらもrandrange()と同じく、整数をランダムに選んで返す関数です。randrange()とのちがいは、上限の値を含むかかどうか、のちがいです。randint(a, b)の場合は、a、bの値をともに含みます。数学的に書けば、「integer n s.t a <= n <= b」となります。

random.choice([リスト])
choice()は、与えたリストの要素からひとつ、ランダムにピックアップして返します。リストだけでなく、文字列、タプルなど、シーケンス型の変数であれば引数に取ることができます。

random.choice([1, 2, 3, 4])  # たとえば3
random.choice(mydict.items())  # items()などと組み合わせれば辞書の要素もランダムで選べます

random.sample([リスト], [整数])
choice()がひとつだけ値を取り出すのに対し、sample()は複数の要素を重複なく取り出してくれる関数です。もしもとのリストで要素が重複していた場合は、重複分だけその要素を取り出す確率が高くなります。

この関数を使えば、たとえば、高校数学で習った組み合わせの問題の「袋に7コの玉が入っています。赤い玉が4つ、白い玉が3つ、その中から3つ取り出したときの・・・」なんてシミュレーションも

random.sample(['r', 'r', 'r', 'r', 'w', 'w', 'w'], 3)  # ['w', 'r', 'w']など

という風に簡単に実現できます。

私は実際に使ったことはありませんが、一様分布だけでなく、正規分布、三角分布、ガウス分布など異なる分布をもつランダム値を扱う関数なんかも用意されています。


インストール
「random」は標準ライブラリに含まれているので、Pythonと別途インストールする必要はありません。


参考
random - Python公式ドキュメント

2013/04/01

Python の例外処理

Python における例外処理について説明してみたいと思います。

Python では、例外処理「 try ... catch ... 」のロジックは try except というキーワードを使って実現することができます。

以下、最も基本的な形から順に Python での例外処理の方法について見ていきましょう(ここでご紹介する記法は Python 2.x のものです。 Python 3.x の場合は少し異なるのでご注意ください)。

try - except


最も基本的な形は tryexcept を使ったパターンです。

try:
    f = open('sample.txt', 'r')
except:
    print 'cannot open sample.txt'

この場合、 try のブロックの中にある処理の中で例外(エラー)が発生したら except ブロックへと処理が移ります。

try ブロックの中で例外が発生しなかった場合は、 except のブロックは実行されず、その後の処理へと進んでいきます。


try - except ExceptionName


キーワード except に後にスペースを挟んで例外のクラスを指定すると、キャッチする例外を絞り込むことができます。

try:
    f = open('sample.txt', 'r')
except IOError:
    print 'cannot open sample.txt'

このコードの場合だと、入出力に関わる IOError という例外のみがキャッチされます。その他の例外があがった場合には except ブロックの処理は実行されません。

exept ブロックは複数個セットすることができます。例えば、次のようにすると、 PermissionError
あがったときとその他の例外があがったときとで表示するメッセージを切り替えることができます。

import sys

try:
    f = open('sample.text', 'r')
except PermissionError:
    print 'パーミッションエラーです。'
except Exception:
    print 'その他の例外です。'

except ブロックは上から順番に一致チェックがかかり、マッチした最初の except ブロックのコードのみが実行されることに注意してください。

つまり、上のコードの場合だと、 try ブロックで PermissionError があがった場合には「パーミッションエラーです。」というメッセージのみが表示され、「その他の例外です。」というメッセージは表示されません。処理の流れのイメージとしては、 if ... elif ... elif に近いイメージです。

上から順にチェックがかかるので、プログラマーは「捕捉範囲の狭い例外を先に、範囲の広い例外を後に記述すること」を心がける必要があります。具体的には、 Python では例外はクラスとして表現されているので、これは「子クラスを先に、親クラスを後に記述すること」を意味します。

ちなみに Python 2.x の場合、組み込みの例外には次のような親子関係があるので、複数の except ブロックを書く場合は、これを確認した上で記述するようにするとよいでしょう。

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StandardError
      |    +-- BufferError
      |    +-- ArithmeticError
      |    |    +-- FloatingPointError
      |    |    +-- OverflowError
      |    |    +-- ZeroDivisionError
      |    +-- AssertionError
      |    +-- AttributeError
      |    +-- EnvironmentError
      |    |    +-- IOError
      |    |    +-- OSError
      |    |         +-- WindowsError (Windows)
      |    |         +-- VMSError (VMS)
      |    +-- EOFError
      |    +-- ImportError
      |    +-- LookupError
      |    |    +-- IndexError
      |    |    +-- KeyError
      |    +-- MemoryError
      |    +-- NameError
      |    |    +-- UnboundLocalError
      |    +-- ReferenceError
      |    +-- RuntimeError
      |    |    +-- NotImplementedError
      |    +-- SyntaxError
      |    |    +-- IndentationError
      |    |         +-- TabError
      |    +-- SystemError
      |    +-- TypeError
      |    +-- ValueError
      |         +-- UnicodeError
      |              +-- UnicodeDecodeError
      |              +-- UnicodeEncodeError
      |              +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
       +-- ImportWarning
       +-- UnicodeWarning
       +-- BytesWarning

Python 2.x 、 3.x それぞれの組み込みの例外について詳しくは公式ドキュメントを参照ください。

6. Built-in Exceptions — Python 2.7 documentation
5. Built-in Exceptions — Python 3.x documentation


try - except ExceptionName, var


キャッチした例外の中身を確認したい場合は、 except の行を exept 例外クラス, 変数名 と書く形になります。

try:
    f = open('sample.txt', 'r')
except Exception, e:
    print e, 'error occurred'

必ずそうしないといけない決まりはありませんが、変数名には exception または省略形の e が選ばれていることが多いようです。

追記 20171222: Python 3.x の場合はここの記法が少し異なり、次のように記述する形になっています。

try:
    f = open('sample.txt', 'r')
except Exception as e:
    print(e, 'error occurred')


try - except - else


except ブロックの後には、オプションで else ブロックをつけることができます。

try:
    f = open('sample.txt', 'r')
except:
    print 'cannot open sample.txt'
else:
    lines = f.readlines()

else ブロックに記述された処理は、 try ブロックの中で例外が発生しなかった場合にのみ実行されます。


try - except - finally


else ブロックと同じく、オプションで finally ブロックをつけることができます。

finally ブロックの中の処理は、 try ブロック内での例外の発生の有無にかかわらず実行されます。

try:
    f = open('sample.txt', 'r')
except:
    print 'cannot open sample.txt'
finally:
    f.close()

elsefinally の両方を記述する場合は、 else を先に、 finally を後に記述するルールになっています。

try:
    # 例外が発生するかもしれない処理
except:
    # 例外が発生したときの処理
else:
    # 例外が発生しなかったときの処理
finally:
    # 例外の発生の有無にかかわらず必ず実行されるべき処理

finally ブロックは基本的には try から始まる一連の処理の最後に実行されますが、 try のブロックの中で例外が発生したのにそれががどの except でもキャッチされなかった場合には finally ブロックの処理が実行された後に try の中であがった例外が再度投げ直されるようになっています。

また、 try ブロックの中で break continue return のいずれかが実行されたときにはそこから脱出する間際に finally ブロックが実行されるようになっているようです。

このあたりの流れは少し複雑なので、 finally を正しく使いたい場合には 公式のドキュメント などをきちんと確認したうえで使うようにされるのがよいかと思います。

以上です。


例外処理に関しては、次のように PEP20 Zen of Python でも言及されています。

Errors should never pass silently.
Unless explicitly silenced.

このあたり、 Python の言語設計のベースの考え方とも直結しているみたいです。


参考

エラーと例外 - Python公式ドキュメント
例外の種類 - Python公式ドキュメント