2.4への機能強化で広がるPythonの世界UNIX USER2005年2月号特別企画より転載(2/4 ページ)

» 2005年01月24日 16時00分 公開
[磯 蘭水,UNIX USER]
Python 2.4の新しい機能の紹介

 前口上が長くなりましたが、いよいよPython 2.4で新たに追加されたり、以前のバージョンから向上した機能のうち、プログラムを書くうえでとくに便利になったと思われる部分を、筆者の独断と偏見でチョイスしていきましょう。

集合を表現するオブジェクト

 以前のバージョンでは、モジュールとして提供されていた集合を表現するための機能が、Python 2.4では、集合を表現するためのsetという型として組み込まれました(実行例1、図1)。

 これらの集合演算は組み込み型としてCで実装されており、高速に動作します。setにはこのほかにも、要素の追加や削除などのメソッドが存在し、集合の演算が非常に便利になりました。

実行例1 集合を表現するための型setの利用例
>>> a = set("hello world")
>>> a
set([' ', 'e', 'd', 'h', 'l', 'o', 'r', 'w'])
>>> b = set("good bye")
>>> 'w' in a
True
>>> a & b
set([' ', 'e', 'd', 'o'])
>>> a ^ b
set(['b', 'g', 'h', 'l', 'r', 'w', 'y'])
>>> a | b
set([' ', 'b', 'e', 'd', 'g', 'h', 'l', 'o', 'r', 'w', 'y'])

図1 図1 集合の表現例(クリックで拡大します)

整数型の統合

 Python 2.4から、通常の整数型と多倍長整数型が統合され、シームレスに扱えるようになりました。たとえば、Python 2.3以前の場合、2の32ビット左シフトの結果は次のようにオーバーフローによって0となります。

>>> hex(2 << 32)
'0x0'

 Python 2.4では、必要に応じて、整数は自動的に多倍長整数に変換されるようになりました。

>>> hex(2 << 32)
'0x200000000L'

 これによって、大きな数を扱う演算について、型を意識してコードを書く手間が省けます。

ジェネレータの生成構文

 Pythonには、リストを作成する場合に便利な、リスト内包表現という構文があります。たとえば下の例では、[1,2,3,4,5]という数列のうち偶数だけを取り出して、新しいリストを作成しています。

>>> [x for x in (1,2,3,4,5) if x % 2 == 0]
[2, 4]

 この構文をうまく使うと、いままでfor、あるいはwhileとifを組み合わせて記述していた部分が簡潔に表現できます。リスト内包表現は便利な構文ですが、出力としてリストを作成しますので、その分メモリを必要とします。実行例2を実行すると、lstは5万個の要素を持つリストを指すことになります。

実行例2 lstは5万個の要素を持つリストを指すことになる
>>> lst = [x for x in range(100000) if x % 2 == 0]

 つまり、大きな結果を返すリスト内包表現はメモリ効率が良くありません。Python 2.4では、このような問題を解決できるジェネレータ式という構文が追加されました(実行例3)。

実行例3 Python 2.4で追加された構文「ジェネレータ式」の例用例
>>> gen = (x for x in range(100000) if x % 2 == 0)
>>> gen <generator object at 0x402db44c>

 これは、リスト内包表現の構文そのままで、囲む記号を「[]」から「()」に変更しただけです。この結果、作成されるオブジェクトはジェネレータ1個になります。ジェネレータにはnext()というメソッドがあり、このメソッドは呼ばれるたびに並びの要素を1つずつその場で生成して返します(実行例4)。つまり、並び全体を格納しておくメモリ領域が不要になり、リーズナブルに利用できるようになります。

実行例4 ジェネレータにはnext()というメソッドがあり、呼ばれるたびに並びの要素を1つずつその場で生成して返す
>>> gen = (x for x in range(5) if x % 2 == 0)
>>> gen.next()
0
>>> gen.next()
2
>>> gen.next()
4
>>> gen.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
StopIteration

 next()メソッドは、返す値がなくなるとStopIterationという例外を発生します。これは繰り返しを終了させる例外ですので、ジェネレータは実行例5のようにfor文の中で利用できます。

実行例5 ジェネレータをfor文の中で利用した場合
>>> gen = (x for x in range(5) if x % 2 == 0)
>>> for i in gen:
...     print i + 1,
...
1 3 5

Templateによる文字列の置き換え機能

 Pythonには、以前から、名前をベースにした便利な文字列の置き換え機能が備わっています。実行例6のように、文字列中に「%(名前)データ型」という形式でプレースホルダーを書いておき、後から%演算子を使って埋め込むデータを流し込めます。しかし、この構文では流し込むデータ型を明示しなくてはならず、型が違う場合には実行例7のようにエラーになってしまいます。また、埋め込むべきデータがない場合もエラーになります(実行例8)。

実行例6 名前をベースにした文字列の置き換え機能
>>> import time
>>> msg = "Hello %(name)s , it's %(hour)i o'clock."
>>> msg % {"hour": time.localtime().tm_hour, "name": "James Random Hacker"}
"Hello James Random Hacker , it's 20 o'clock.

実行例7 文字列の置き換え機能を利用する際、型が違う場合にはエラーになる
>>> msg % {"name": "James Random Hacker", "hour": "20"}
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: int argument required

実行例8 埋め込むべきデータがない場合もエラーになる
>>> msg % {"name": "James Random Hacker", "month": "12"}
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
KeyError: 'hour'

 Python 2.4では、$と辞書を使った書式化を行える、より柔軟なTemplateクラスが追加されました。Templateクラスはstringモジュール内にあり、実行例9のように利用できます。このように、データ型に依存しない書き方ができるようになっています。

実行例9 Python 2.4で追加されたTemplateクラス
>>> import string
>>> msg = string.Template("Hello $name , it's $hour o'clock")
>>> msg.substitute({"name": "James Random Hacker", "hour": time.localtime().tm_hour})
"Hello James Random Hacker , it's 20 o'clock"
>>> msg.substitute({"name": "James Random Hacker", "hour": "01"})
"Hello James Random Hacker , it's 01 o'clock"

 substituteメソッドでは、埋め込むべきデータがない場合は実行例10のようにエラーになります。これを無視するには実行例11のようにsafe_substitute() メソッドを使います。このように存在しないデータについては、未処理のまま結果を返します。さらにこの結果を、再びstring.Template()でテンプレートにして、段階的に最終的な出力を作成していけるようになりました。

実行例10 substituteメソッドでは、埋め込むべきデータがない場合はエラーになる
>>> msg.substitute({"name": "James Random Hacker", "month": "12"})
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "/opt/Python-2.4/lib/python2.4/string.py", line 172, in substitute
    return self.pattern.sub(convert, self.template)
  File "/opt/Python-2.4/lib/python2.4/string.py", line 162, in convert
    val = mapping[named]
KeyError: 'hour'

実行例11 実行例9のようなエラーを無視するにはsafe_substitute()メソッドを使う
>>> msg.safe_substitute({"name": "James Random Hacker", "month": "12"})
"Hello James Random Hacker , it's $hour o'clock"

Copyright(c)2010 SOFTBANK Creative Inc. All rights reserved.

注目のテーマ