はじめに

 このシリーズの第1回では、コア言語と型システムの重要な変更点について解説しました。今回は、Python 3.0における数値、文字列、バイナリデータの基本データ型の処理を中心に説明します。

PEP 237:長整数型と整数型の統合

 Python 2.xには、intlongの2つの整数型がありました。int型は、マシンの「ネイティブ」ワードサイズ(現在の機種では32ビットまたは64ビット)に制限されます。そのためint型の演算では、オーバーフローしてOverflowError例外が発生する可能性がありました(Python 2.2より前のバージョン)。これに対し、long型はメモリ容量のみに制限され、理論的にはあらゆる整数を表現できます。

 2種類の整数型が用意されていた理由は、int型とlong型にそれぞれ異なる特徴があったからです。int型はハードウェアとOSで直接サポートされるため非常に効率的であり、一方、long型は柔軟性があって開発者が値のサイズを監視する必要がありません。しかし、アーキテクチャの異なるマシン間でPythonのコンパイル済みファイルやpickle化オブジェクトを移植する場合には、型が2種類あることで問題が発生します。

 PEP-237の目的は、これら2つの型を1つの整数型に統合し、可能な場合にはより効率的なマシン整数型を使用するよう内部的に変化させることです。これは、実のところ4つのバージョンで段階的に実装され、2.2、2.3、2.4を経て3.0で完了しました。

 Python 2.4以上では、例外も警告も発生させない、intからlongへの自動昇格が既にサポートされています。Python 3.0では、単にPythonレベルでlong型とlongリテラルをなくしただけです。Python 3.0でlongを使用しようとすると、次のようなエラーが発生します。

>>> long
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'long' is not defined
 Python 3.0では、long用のLサフィックスも削除されました。現在は整数はすべてint型です。Python 2.5では次のコードは有効ですが、

>>> 5L
5L
 Python 3.0では構文エラーになります。

>>> 5L
  File "<stdin>", line 1
    5L
     ^
SyntaxError: invalid syntax
 Python 2.5では、次のコードはlongオブジェクトを生成しますが、

>>> x = 5 ** 88
>>> type(x)
<type 'long'>
>>> x
32311742677852643549664402033982923967414535582065582275390625L
 Python 3.0ではintになります。

>>> x = 5 ** 88
>>> type(x)
<class 'int'>
>>> x
32311742677852643549664402033982923967414535582065582275390625

PEP 3127:整数リテラルのサポートと構文

 Pythonでは以前から、整数の基数を広範にサポートしてきました。Python 2.5のint()関数とlong()関数は、第2引数として変換元の基数を受け取ります。この基数には、2?36の整数を指定できます。

>>> int('000111', 2)
7

>>> int('000111', 1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: int() base must be >= 2 and <= 36

>>> int('000111', 36)
1333
TypeError: long() can't convert non-string with explicit base

>>> long('555555555555555555555555555555555555555', 6)
2227915756473955677973140996095L
 Python 3.0でもこの機能は維持されています(ただし、エラーメッセージでは、baseの代わりにarg 2と示されます)。

>>> int('0001111', 2)
15

>>> int('5', 36)
5

>>> int('5', 37)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: int() arg 2 must be >= 2 and <= 36
 Python 2.5では、8進と16進の整数リテラルもサポートしており、整数を指定するところでは、8進値または16進値も指定できます。8進値には0123のように先頭に数字のゼロを付け、16進値には0x123のように先頭に数字のゼロと文字xまたはXを付けます。また、oct()hex()という2つの関数もあります。どちらも整数を受け取り、それぞれ8進値または16進値の文字列表現を返します。次に例を示します。

>>> 010
8
>>> 010 + 8
16
>>> 0xa
10
>>> 0xa + 010 + 2
20
>>> oct(20)
'024'
>>> hex(20)
'0x14'
 Python 3.0でもこれらの機能はすべて維持されますが、1つだけ小さな変更があります。8進値のプレフィックスは、先頭に数字のゼロと文字oまたはOを付けるように変更されました。従って、これまで0123のように書いていたものが0O123という表記になります。数字のゼロ1文字を付けるという以前の表記は、C言語に由来する表記です。今回の変更は、C系列の言語や8進値になじみのない開発者の混乱を防ぐ効果があります。そのような開発者は、先頭にゼロを付けても値は変わらないと誤解しやすく、例えば、書式を整えたり、インデントを揃えたりする目的で先頭にゼロを付けることで、予期しない値を設定してしまう可能性がありました。また、Python 3.0では、2進リテラルも追加されました。このように、Cの遺産から脱却することによって、整数リテラルの基数表現が統一され、基数2、8、16(2進数、8進数、16進数)のプレフィックスは、それぞれ0b0o0xとなります。

>>> 0b10
2

>>> 0o10
8

>>> 0x10
16
 また、新しいbin()関数も追加されました。これは、整数を2進文字列表現に変換します(oct()hex()と同様の機能です)。

>>> bin(5)
'0b101'

>>> bin(0x10)
'0b10000'

>>> bin(0o10)
'0b1000'
 当然、oct()関数でも、Python 2.5の0プレフィックスではなく、新しい0oプレフィックスが使用されます。

>>> oct(12)
'0o14'
 この変更は、全体から見れば小さな変更ですが、すっきりと洗練された、いいことずくめの解決策だと思います。新しいユーザーの障害を取り除き、過去(C言語の8進表記)のしがらみから離れ、その上、基数リテラルの表現が統一されたことで、新しい2進リテラルの追加にとっても重要な改善となっています。

PEP 238:除算演算子の変更

 Python 2.xでは、除算演算は整数の除算と浮動小数点数/複素数の除算に分かれていました。整数の除算は切り捨て除算で、除算結果は最も近い整数(intまたはlong)になるように切り捨てられます。

>>> 5 / 2
2
>>> -5 / 2
-3
 浮動小数点数と複素数の除算は、いわゆる「真の除算(true division)」であり、算術演算として妥当な近似値が返されます。

>>> 5.0 / 2
2.5
>>> -5 / 2.0
-2.5
>>> complex(5, 0) / 2
(2.5+0j)
 2種類の除算が存在するのは、言語のデザインにおいて非常に深刻な問題と言えます。整数と浮動小数点数や複素数の演算を行うアルゴリズムを作成する場合は、非常に厄介なことになります。例えば関数ライブラリを開発するときに、真の除算を行うようにしたいと思っても、どうすれば安全に実現できるのか、すぐには明確な答えが見つかりません(__future__による回避策については後述します)。

 例えば、整数、浮動小数点数、複素数の平均値を返す関数を作成するとします。単純な方法として次のようなコードが考えられます。

def average(*numbers):
  return sum(numbers) / len(numbers)
 対象となるすべての数値が整数で、平均値が整数でない場合には、この関数は失敗します。

>>> average(1,4)
2
 では、どのようにすれば真の除算ができるのでしょうか。すべての引数を浮動小数点数型に変換するという方法では、複素数があると対応できません。すべての数値に0.0を足すという方法では、元の数値が浮動小数点数-0.0の場合に符号が消えてしまいます(IEEE 754の浮動小数点数標準によると、0.0と-0.0は同一値ではありません)。

>>> x=-0.0 
>>> x
-0.0
>>> x + 0.0
0.0
 型と符号を維持し、かつ、真の除算を行う唯一安全な方法は、引数を1.0倍することです。この方法は、単純なaverage関数を作成しようとするときに容易に考えつく方法とは言えず、無視できないオーバーヘッドもあります。

def average(*numbers):
  return sum([n * 1.0 for n 
     in numbers]) / len(numbers)
  
>>> average(1,4)
2.5
 幸い、Python 3.0では、除算は常に真の除算となり、(結果が整数でも)浮動小数点数または複素数(オペランドに複素数がある場合)を返します。

>>> 5 / 2
2.5
>>> -5 / 2
-2.5
>>> 4 / 2
2.0
>>> complex(5, 0) / 2
(2.5+0j)
 Python 3.0で切り捨て除算を行うには、//演算子を使用します。両方のオペランドが整数の場合、結果は整数になります。オペランドの一方が浮動小数点数で、もう一方が浮動小数点数または整数の場合、結果は浮動小数点数(ただし、整数と等しい浮動小数点数)になります。切り捨て除算は複素数には対応しません。

>>> 5 // 2
2
>>> 5.0 // 2
2.0
>>> complex(5,0) // 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't take floor of a complex number.
 あまり知られていませんが、明示的切り捨て除算は、Python 2.2から使用できました。また、真の除算の動作も、Python 2.2で次のステートメントを使用することによって利用できます。

from __future__ import division
 これは、Python 3.0へ移行するコードを段階的に準備する場合に便利です。数値処理コードがある場合、またはPython 2.x方式の除算を行うコードを呼び出す場合、厄介なバグが発生する可能性があります。除算演算子を実際に使用しているコードを変更するだけではなく、呼び出し側もすべて確認しなければならなくなるからです。Python 3.0移行時の除算演算子対策に関するさらに具体的な情報については、今後の記事で取り上げる予定です。

 インタープリタに-Qコマンドライン引数を渡して、除算の動作を制御することもできます。この引数に指定できる値は、old(デフォルト)、warnwarnallnew(真の除算)です。ただし、これを行う意味と理由を十分に理解している場合を除き、この方法はお勧めしません。除算のような重要な動作の制御をコマンドライン引数に依存するのは、極めて危険です。

浮動小数点数の改善点

 却下されたPEP-754では、PythonのIEEE 754完全準拠が提案されていました。これが却下されたのは、NaN(非数)や正負の無限大など、IEEE 754特殊値を処理するのに、Python(CPython実装)がCライブラリに依存しているためです。異なるプラットフォーム間に多くの不整合があります。それでも、Python 3.0では(2.6でも)浮動小数点数に多くの改良が加えられ、IEEE 754標準の実装も大幅に進みました。

 文字列を浮動小数点数に変換するfloat()関数は、nan+inf(またはinf)、および-infを認識し、IEEE 754の非数、正の無限大、負の無限大に変換するようになりました(大文字小文字は区別されず、NaNINFなども有効です)。

 mathモジュールに、isnan()関数とisinf()関数が追加されました。isinf()関数では、inf+inf-infは区別されません。以下に使用例を示します。

>>> float('nan')
nan

>>> float('NaN') # Any case works
nan

>>> float('+inf')
inf

>>> float('-inf')
-inf

>>> float('INF')
inf

>>> float('nan') + float('inf')
nan

>>> float('inf') + float('-inf')
nan

>>> float('inf') - float('-inf')
inf

>>> import math
>>> math.isnan(float('nan'))
True

>>> math.isinf(float('inf'))
True

>>> math.isinf(float('-inf'))
True

>>> math.isinf(float('nan'))
False

>>> math.isnan(float('-inf'))
False
 mathモジュールには、copysign(x, y)関数も追加されました。この関数は、xの絶対値に符号yを付けて返します。-1と1を返す単純なsign()や、ispositive()isnegative()といった関数ではなく、どうしてこの関数なのか、よく分かりません。ドキュメントにもごく簡潔な記述しかありません。

>>> help(math.copysign)
Help on built-in function copysign in module math:

copysign(...)
    copysign(x,y)
    
    Return x with the sign of y.
 とはいえ、copysign()関数はこの説明のとおりに動作します。ただし、NaNの場合は別です。NaNの符号をコピーしようとした場合の結果は一貫せず、Mac OS Xでは負の符号、Windowsでは正の符号になります。クローズ済みのバグによると、この動作で問題なし、とされていますが、同意できません。NaNは非数なので、符号はありません。NaNから符号をコピーしようとすることは、数値ではないそれ以外の値(文字列、リスト、オブジェクト)から符号をコピーしようとするのと同じであり、例外を発生させるべきです。

 mathモジュールには、浮動小数点数関連の関数がこれ以外にも追加されています。math.fsum()は、繰り返し可能オブジェクト(iterable)から数値のストリームの合計を出す関数で、sum()組み込み関数とは異なり、部分合計による精度ロスを入念に回避しています。いずれかの数値がNaNの場合、結果はNaNになります。部分合計が+infまたは-infに達すると、sum()関数はそれを結果として返しますが、math.fsum()関数はOverflowError例外を発生させます。これは、よりIEEE 754に沿った動作です。

>>> sum([1e308, 1, -1e308])
0.0

>>> math.fsum([1e308, 1, -1e308])
1.0

>>> sum([1e100, 1, -1e100, -1])
-1.0

>>> math.fsum([1e100, 1, -1e100, -1])
0.0

>>> x = [1e308, 1e308, -1e308]
>>> sum(x)
inf

>>> math.fsum(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: intermediate overflow in fsum

>>> sum([float('nan'), 3.3])
nan

>>> math.fsum([float('nan'), -float('nan')])
nan
 acosh()関数、asinh()関数、atanh()関数は、逆双曲線関数を計算します。log1p()関数は1+x (base e)の自然対数を返します。trunc()関数は、数値の小数点以下を切り捨てて近似値の整数値を返します。

>>> math.acosh(30)
4.0940666686320855

>>> math.acosh(1)
0.0

>>> math.asinh(1)
0.88137358701954305

>>> math.asinh(0)
0.0

>>> math.atanh(0.5)
0.54930614433405489

>>> math.log1p(2)
1.0986122886681098

>>> math.trunc(-1.1)
-1
>>> math.trunc(-1.9)
-1
>>> math.trunc(1.1)
1
>>> math.trunc(1.9)
1
>>> math.trunc(3.0)
3
 浮動小数点数と16進文字列の間の変換を行うことができます。変換関数は、浮動小数点数を文字列表現へ、文字列表現を浮動小数点数へ変換し、10進と2進の変換の丸め誤差を発生させません(その数値を完全に表現する桁数がある場合)。floathex()メソッドは文字列を返し、float.fromhex()メソッドは文字列を(可能な限り正確に)数値に変換します。

>>> x = 4.2
>>> a.hex()
'0x1.0cccccccccccdp+2'
>>> float.fromhex('0x1.0cccccccccccdp+2')
4.2000000000000002
 decimalモジュールは汎用10進演算仕様バージョン1.66に更新されました。新しい機能として、exp()log10()などの基本的な算術関数のメソッドが追加されました。

>>> Decimal(1).exp()
Decimal("2.718281828459045235360287471")

>>> Decimal("2.7182818").ln()
Decimal("0.9999999895305022877376682436")

>>> Decimal(1000).log10()
Decimal("3")
 Decimalオブジェクトのas_tuple()メソッドは、符号、仮数、指数のフィールドを持つ名前付きタプルを返すようになりました(名前付きタプルについては今後の記事で説明します)。

>>> Decimal('-3.3').as_tuple()
DecimalTuple(sign=1, digits=(3, 3), exponent=-1)
 sysモジュールの新しい変数float_infoは、float.hファイルから得たプラットフォームの浮動小数点数サポート情報が含まれるオブジェクトです。

>>> sys.float_info
sys.floatinfo(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, 
min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, 
mant_dig=53, epsilon=2.2204460492503131e-16, radix=2, rounds=1)
 このように、Pythonの浮動小数点数サポートは確かに向上しましたが、まだ完璧ではありません。Pythonで重要な数値演算を行うには、Numpy外部パッケージが依然として最善の策と言えます。

PEP 3141:数値の型階層

 Python 3.0では、numbersというモジュールが導入されました(Python 2.6にもあります)。このモジュールには、各数値型の抽象ベースクラス(ABC)が含まれています。これによって実質的に数値型の階層が形成され、各数値型は上位の数値型クラスのサブクラスになります。この階層は、SchemeのNumeric Towerをヒントにしています。NumberComplexRealRationalIntegralの5種類のABCがあります。それぞれのクラス(Numberを除く)は前のクラスのサブクラスです。例えば、IntegralRationalのサブタイプです。すべての整数Xは有理数X/1、つまり、Xを分子、1を分母とする有理数と見なすことができます。

 ベースクラスNumberは、実際のどの数値型とも対応していません。このクラスは、ある値が数値であるかどうかを検査する場合に備えて用意されています。通常、数値かどうかを調べるには、numbers.Complexのサブクラスであることを確認できれば十分ですが、NumberクラスとComplexクラスの間に、Complexのサブクラスではない新しい数値を作成するようなまれなケースでは、Numberクラスが必要になります。

 ComplexRealIntegralのABCは、それぞれcomplex型、float型、int型によって実装されています。

>>> issubclass(int, numbers.Integral)
True

>>> issubclass(float, numbers.Real)
True

>>> issubclass(complex, numbers.Complex)
True

>>> issubclass(complex, numbers.Real)
False
 RationalのABCは、新しいfractionsモジュールのfractions.Fraction型によって実装されています。このモジュールには、最大公約数を求めるためのgcdメソッドと、変換関数from_float()from_decimal()もあります。言語に有理数の数値型を追加する方がすっきりして一貫性があると思われますが、モジュールの1クラスになりました。

>>> issubclass(fractions.Fraction, numbers.Rational)
True

>>> fractions.gcd(Fraction(1,3), Fraction(1,2))
Fraction(1, 6)

>>> fractions.gcd(Fraction(2,6), Fraction(2,3))
Fraction(1, 3)

>>> fractions.gcd(6,9)
3

>>> Fraction.from_float(0.5)
Fraction(1, 2)

>>> import math
>>> Fraction.from_float(math.pi)
Fraction(884279719003555, 281474976710656)

>>> Fraction.from_decimal(decimal.Decimal('3.5'))
Fraction(7, 2)
 固定小数点数と浮動小数点数の算術用のdecimalモジュールのDecimalクラス(Python 2.4で導入されました)は、これには関与せず、数値のABCクラスを実装していません。PEP-3141によると、decimalモジュールの開発者たちと相談した結果、今回は導入を見送る方がよいと決定されたとのことです。

 数値型がこのように準形式化されたことによって、特定の数値型を引数とする関数が可能になり、引数の検査がより容易になりました。

 例えば、ある数値にその数値を乗算するsquare()という関数を作成するとします。ただし、演算は実数の範囲内とし、結果は常に正の値になるものとします。型を検査しない単純な実装は次のようになります。

def square(x):
  return x * x
 このコードでは、複素数を指定したとき、正の値が返されません。

>>> square(complex(0,1))
(-1+0j)
 引数がintfloatであることを明示的に検査しようとすると、あまり洗練されていないコードになります。有理数にも対応しようとすればなおさらです。

>>> def square(x):
...   assert type(x) == int or \
...          type(x) == float or \
...          type(x) == Fraction
...   return x * x
... 
>>> from fractions import Fraction
>>> square(5)
25

>>> square(4.4)
19.360000000000003

>>> square(Fraction(1,3))
Fraction(1, 9)

>>> square(complex(0,1))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in square
AssertionError
 この場合、既存の型またはnumbersモジュールのいずれかのABCクラスからサブクラス化して、独自の数値型を作るという方法があります。そうすれば、その新しい独自の数値型を受け取るコードで、渡された引数をABCに基づいて検査できます。これは、例えば、特定範囲内の整数、または有効桁数が限られた固定小数点セマンティクスの実数を処理する場合に有効です。ABC準拠のクラスには大量のメソッドの実装が必要になるので、ここでは完全なコード例は示しません。

すべての文字列のUnicode化

 Python 2.xには、バイト文字列(str)とUnicode文字列(unicode)の2種類の文字列型がありました。バイト文字列には、バイト(通常デフォルトロケールに基づいてPythonで解釈されます)が含まれ、Unicode文字列には当然Unicode文字が含まれます。

>>> s = 'hello'
>>> u = u'\u05e9\u05dc\u05d5\u05dd'

>>> type(s)
<type 'str'>

>>> type(u)
<type 'unicode'>
 strunicodeも、共通のベースクラス「basestring」から派生しています。

>>> unicode.__bases__
(<type 'basestring'>,)

>>> unicode.__base__ 
<type 'basestring'>
>>> str.__bases__
(<type 'basestring'>,)
 Python 3.0では、すべての文字列がUnicodeになりました。str型は、Python 2.xのunicode型と同じセマンティクスとなり、別個のunicode型はなくなりました。basestringベースクラスもなくなりました。

>>> s = '\u05e9\u05dc\u05d5\u05dd'
>>> type(s)
<class 'str'>
 Python 2.xのバイト文字列は、bytesbytearrayの2種類の型になりました。バイト配列には、変更可能と変更不能の両方があります。bytes型では、次のように文字列操作の多数のメソッドがサポートされています。

>>> dir(bytes)
['__add__', '__class__', '__contains__', 
'__delattr__', '__doc__', '__eq__', '__format__', 
'__ge__', '__getattribute__', '__getitem__', 
'__getnewargs__', '__gt__', '__hash__', '__init__', 
'__iter__', '__le__', '__len__', '__lt__',
 '__mul__', '__ne__', '__new__', '__reduce__', 
'__reduce_ex__', '__repr__', '__rmul__', 
'__setattr__', '__sizeof__', '__str__', '__subclasshook__', 
'capitalize', 'center', 'count', 'decode', 'endswith', 
'expandtabs', 'find', 'fromhex', 'index', 'isalnum', 
'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 
'isupper', 'join', 'ljust', 'lower', 'lstrip', 
'partition', 'replace', 'rfind', 'rindex', 'rjust', 
'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 
'startswith', 'strip', 'swapcase', 'title', 
'translate', 'upper', 'zfill']
 bytearray型にも、変換メソッドextend()insert()append()reverse()pop()remove()があります。

 どちらも+演算子と*演算子をサポートしており(文字列と同じセマンティクスを使用)、bytearray型では+=*=もサポートされます。

 bytesbytearrayもエンコードを認識しないのに対し、strオブジェクトにはエンコードが必須なので、エンコードを明示的に指定せずにstrから、またはstrへ変換することはできません。bytesオブジェクトまたはbytearrayオブジェクトをstr()に直接渡すと、repr()の結果が返されます。変換するには、decode()メソッドを使用する必要があります。

>>> a = bytearray(range(48, 58))
>>> a
bytearray(b'0123456789')

>>> s = str(a)
>>> s
"bytearray(b'0123456789')"

>>> s = a.decode()
>>> s
'0123456789'
 文字列からbytesまたはbytearrayに変換するには、文字列のencode()メソッドを使用するか、bytesオブジェクトまたはbytearrayオブジェクトのコンストラクタにエンコードを渡す必要があります。

>>> s = '1234'
>>> s
'1234'

>>> b = bytes(s)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: string argument without an encoding

>>> b = s.encode()
>>> b
b'1234'

>>> b = bytes(s, 'ascii')
>>> b
b'1234'
 文字列表現も変更になりました。Python 2.xでは、repr()の戻り値はstr型で、ASCIIベースの文字列でした。Python 3.0では、戻り値の型はstrのままですが、Unicode文字列になります。文字列表現のデフォルトのエンコードは、出力デバイスによって決まります。

※著者注
 このトピックの詳細については、関連PEPPEP-358PEP-3118PEP-3137PEP-3138を参照ください。

PEP 3101:高度な文字列書式指定

 Python 3.0では、Microsoftの.NET複合書式指定機能に基づいた、新しい強力な文字列書式指定が導入されました。.NET複合書式指定機能を選んだのは慧眼です。これまでさまざまなプログラミング言語の文字列書式指定機能を利用してみて、.NET複合書式指定を採用しているC#の書式指定が1番よかったと感じています。高機能で、柔軟性と一貫性があり、ドキュメントも分かりやすいものになっています。

※著者注
 ところで、PEP-3101にある.NET複合書式指定へのリンクは正しくありません(執筆時点)。正しいURLはhttp://msdn.microsoft.com/en-us/library/txafckwd.aspx、日本語版リンクはhttp://msdn.microsoft.com/ja-jp/library/txafckwd.aspxです。

 Python 2.xでは、文字列書式指定に%演算子またはstring.Templateを使用していました。%演算子は便利で、引数1つだけを書式指定するときはそのまま渡すことができます。
import time
>>> time.localtime()
(2008, 12, 31, 10, 32, 16, 2, 366, 0)
>>> 'The current year is %d' % time.localtime()[0]
'The current year is 2008'
 複数の引数を書式指定するには、それらをタプルまたはリストにまとめる必要があります。

>>> t = time.localtime()
>>> 'Day: %d, Month: %d, Year: %d' % (t[2], t[1], t[0])
'Day: 31, Month: 12, Year: 2008'
 タプルまたはリストを使用する場合、書式指定する順番どおりに引数を指定する必要があります。また、同じ値を複数回表示させるには、複数回書式指定する必要があります。

>>> s = 'The solution to the square of %d is: %d * %d = %d'
>>> s % (5, 5, 5, 5 * 5)
'The solution to the square of 5 is: 5 * 5 = 25'
 または、ディクショナリを渡して、書式指定文字列内でディクショナリキーを指定する方法もあります。

>>> d = dict(n=5, result=5 * 5)
>>> s = 'The solution to the square of %(n)d is: %(n)d * %(n)d = %(result)d'
>>> s % d
'The solution to the square of 5 is: 5 * 5 = 25'
 このように、ディクショナリを使用する場合には、繰り返される値を1度指定するだけで済みますが、それには高い代償もあり、書式指定文字列より複雑になる上、値をただ渡す代わりにdictを準備する必要も生じます。

 最後に、string.Templateクラスを使用する方法があります。これを使用してコンパイル済みのテンプレートを用意すると、異なる値に何回も適用でき、書式指定文字列自体は1回しか解析されないので、効率的です。このことは、テンプレート化されているWebページやコード生成シナリオなど、テスト結果が大きくなったり、書式指定文字列解析の負荷が高くなったりしやすい状況で特に重要になります。書式指定文字列は若干異なります。名前付き値は、先頭に$記号を付け、必要に応じて前後のテキストから区別するために中かっこで囲みます。

>>> s = 'The solution to the square of ${n} is: ${n} * ${n} = ${result}'
>>> t = string.Template(s)
>>> for i in range(1, 7):
...   d = dict(n=i, result=i * i)
...   print t.substitute(d)
... 
The solution to the square of 1 is: 1 * 1 = 1
The solution to the square of 2 is: 2 * 2 = 4
The solution to the square of 3 is: 3 * 3 = 9
The solution to the square of 4 is: 4 * 4 = 16
The solution to the square of 5 is: 5 * 5 = 25
The solution to the square of 6 is: 6 * 6 = 36
 Python 3.0では、stringクラスにformat()という新しい書式指定メソッドが追加されました。これは、短い書式指定文字列用に%書式指定に代わるものとして導入されました。書式指定文字列のコンパイルは行わないので、string.Template書式指定の代わりにはなりません。format()メソッドは、1つの書式指定文字列内で位置指定引数とキーワード引数の両方を認識します。書式指定文字列内の置き換えフィールドは中かっこで囲みます。同じ位置指定引数を異なるフィールドで複数回再使用できます。

>>> s = 'Addition is commutative. For example: {0} + {1} = {1} + {0}'
>>> s.format(5, 7)
'Addition is commutative. For example: 5 + 7 = 7 + 5'

>>> s.format(4, 3, result=3 * 4)
'4 multiplied by 3 is 12'
 中かっこ自体は中かっこ2つで表します。

>>> '{0} "{{", {1} "}}"'.format('open curly:', 'closed curly:')
'open curly: "{", closed curly: "}"'
 format()メソッドでは、文字列や10進整数などの単純なフィールドと、複合フィールドの両方がサポートされます。複合フィールドは、オブジェクト属性や配列要素を使用できるのでとても便利です。

>>> import fractions
>>> r = fractions.Fraction(5, 4)
>>> '{0.numerator} / {0.denominator}'.format(r)
'5 / 4'

>>> 'Day: {0[2]}, Month: {0[1]}, Year: {0[0]}'.format(time.localtime())
'Day: 31, Month: 12, Year: 2008'
 属性や配列要素を使用できると、オブジェクト、タプル、リスト、配列をそのまま指定でき、各部に分解して正しい順番に並べ直す必要がないので、作業が単純になります。前に示したPython 2.xバージョンの例と、上記の例を比べてみてください。

 一部のテンプレート言語とは異なり、書式指定文字列には任意のPython表現は使用できません。Python 3.0書式指定文字列は、オブジェクト、属性、およびタプル/配列/リストのインデックスに限定されます。

 format()メソッドでは、書式指定されたフィールドの表示を細かく制御する広範な書式指定子を使用できます。書式指定子とフィールド名はコロン(:)で区切ります。

'The "{0:10}" is right padded to 10 characters'.format('field')
'The "field     " is right-padded to 10 characters'
 オブジェクトの__format__メソッドに独自の書式指定子を定義、指定できますが(後述の例を参照)、すべてのオブジェクトに適用できる標準指定子も、Pythonで多数用意されています。標準書式指定子の汎用形式は次のようになっています。

[[fill]align][sign][#][0]
  [minimumwidth][.precision][type]
 多数の細かいルールや制約があります。書式指定子によっては、数値型にしか対応しないものや、別の指定子もないと機能しないものもあります。整数と実数には、次のようにさまざまな表示オプションがあります。

>>> '{0:@^8.4}'.format(1 / 3) 
'@0.3333@'
 さて、これはどうなっているのでしょうか。アットマーク(@)は埋め込み文字です。配置として中央揃え(^)が指定され、有効桁数が4、最小幅が8に指定されています。その結果、数値は有効桁数4桁で表され(0.3333)、ゼロと小数点の分、2文字増え、さらに埋め込み文字@が2つ追加されて、合計8文字が中央揃えで表示されます。いずれもPython 2.xの%書式指定より簡潔で、しかもより柔軟性があり高機能です。

 新しい文字列書式指定機能の真の力は、__format__()メソッドを実装して定義するカスタム書式指定で発揮されます。このメソッドのシグネチャは次のとおりです。

def __format__(self, format_spec):
  ...
 さまざまな色による表示を書式指定できるColorStringというクラスを作成するとします。Pythonで、色指定されたテキストなどを画面に出力するには、LinuxとMac OS XではANSIエスケープコードを、32ビットWindowsではSetConsoleTextAttribute() APIを使用できます。

※著者注
 ここに示すコードは、Windowsでは適切に表示されません。色が変わる代わりに、元のテキストの前後に不要な文字が出力されます。
 従って、テキストを赤い色で出力するには、次のようにします。

print('\033[31mRed Text\033[0m')

エスケープシーケンスの使用

 エスケープシーケンスは、ESC+[で始めます(制御シーケンス開始とも言います)。ESC文字は出力不能で、chr(27)または\x1b(16進表記)でも指定できます。033は27の8進表記です。\033[の次の31mは、テキストの色を赤に変更するコードです。次に対象テキスト(Red Text)が続き、最後にテキストの色をデフォルトに戻すコード(\033[0m)を指定します。Pythonの方では8進表記が0(number}から0O{number}に変更になりましたが、ANSIエスケープシーケンスは今も0{number}表記を使用する端末に対応しています。

 エスケープシーケンスはさまざまな操作に使用でき、文字色や背景色の変更、画面上のカーソルの移動(特定の場所への出力)、画面の特定部分の消去、カーソルの表示/非表示、スクリーンバッファのスクロールなどができます。ここでは色の変更の例のみ説明します。

 ある小さなモジュールに、文字列、文字色、背景色の3つの引数を受け取り、該当するANSIエスケープシーケンスで文字列を囲むcolorize()という関数を作成するとします。まず、文字列からANSIエスケープコードにマップされたすべての文字色と背景色を含む、小さなグローバルディクショナリを用意します。関数自体では、文字色と背景色またはその一方がredやgreenのような名前で指定されているかどうかを検査し、ディクショナリから対応するコードを見つけ、指定の色に変更する適切なエスケープシーケンスを用意します。最後にすべてを通常の設定に戻します。ここに示すコードにはエラー処理がないので、指定の色がない場合にはKeyError例外が発生します。

colors = ['black', 'red', 'green', 'orange', 'blue', 'magenta', 'cyan', 'white']
color_dict = {}
for i, c in enumerate(colors):
  color_dict[c] = (i + 30, i + 40)

def colorize(text, color=None, bgcolor=None):
  c = None
  bg = None
  if color is not None:
  c = color_dict[color][0]
  if bgcolor is not None:
    bg = color_dict[bgcolor][1]

  s = ''
  if c is not None:
    s = '\033[%dm' % c
  if bg is not None:
    s += '\033[%dm' % bg
  return '%s%s\033[0m' % (s, text)
 このコードを使って、さまざまな文字色と背景色の出力を試すことができます。以下は、白のテキストとマゼンタの背景を出力する例です。

print(colorize('White on Magenta', 'white', 'magenta'))
 このコードとcolorizeモジュールは、Python 2.xと3.0のどちらでも動作します。

 colorize()関数を習得すれば、それ自体の色を書式指定するColorStringクラスも作成できます。その基本的な方法は、組み込みのstrクラスからサブクラスを作成し、__format__()メソッドを追加し、このメソッドが受け取るformat_specを文字色としてcolorize()関数に渡し、その関数から囲まれた文字列を返す、というものです。

class ColorString(str):
  def __format__(self, format_spec):
    s = colorize(self, format_spec)
    return s
 この実装では、背景色は変更できず、文字色の変更だけですが、書式指定が非常に簡潔になります(色名を指定するだけです)。以下は、ColorStringの使用例です。この例では、まず、1つの文「'Yeah, it works!'」を分割してColorStringの単語のリストを用意し、次にred、green、blueという色の書式指定文字列を指定して、各単語をそれぞれ異なる色で出力します。

words = [ColorString(x) for x in 'Yeah, it works!'.split()]
print('{0:red} {1:green} {2:blue}'.format(*words))
 Python 3.0には、単一のオブジェクトの書式指定に使用するformat()グローバル関数もあります。これは単純にオブジェクトの__format__()メソッドを呼び出します。ColorStringの例では次のようになります。

>>> format(ColorString('Gigi'), 'red')
'\x1b[31mGigi\x1b[0m'
 このサブクラス化スキームは有効に機能しますが、カスタム書式指定を行うたびに、__format__()メソッドを持つ特別なクラスを作成するのは、少し面倒な気もします。また、このようなサブクラス化では、書式指定を利用する開発者がColorStringのような特別なオブジェクトを作成する必要が生じます。幸い、独自の書式指定クラスを実装してあらゆる型の書式指定に使用することもできます。例えば、単にテキストを指定の色で出力できれば便利です。以下はColorFormatterというクラスの例です。このクラスはstring.Formatterクラスのサブクラスで、format_fieldメソッドをオーバーライドします。このオーバーライドでは、colorsリスト内にformat_specがあればフィールドの色を変更し、なければFormatter.format_field()を呼び出してデフォルトの書式指定を適用します。

from string import Formatter

class ColorFormatter(Formatter):
  def format_field(self, value, format_spec):
    if format_spec in colors:
      return colorize(value, format_spec)
    else:
      return Formatter.format_field(self, value, format_spec)
 カスタム書式指定クラスを使用するには、それをインスタンス化し、format()メソッドを呼び出して書式指定された文字列を取得する必要があります。これをもっと簡潔にするために、バインドされたformat()メソッドをfという変数に割り当てると、さらに使いやすくなります。

formatter = ColorFormatter()
f = formatter.format

print(f('{0:cyan} works very {1:orange}.', 'ColorFormatter', 'well'))
 フィールド値のリストまたは名前付きフィールドのディクショナリがある場合には、vformat()メソッドを使用できます。このメソッドは、位置指定引数としてリストを、キーワード引数としてディクショナリを受け取ります。

formatter = ColorFormatter()
f = formatter.vformat

args = ['The', 'vformat()']
kwargs = dict(m='method', t='too')
print(f('{0:red} {1:blue} {m:green} works {t:magenta}', args, kwargs)

まとめ

 Windows開発者がPython 3.0を少し勉強したいという場合は、色のANSIエスケープコードをWindowsに実装する、新しい代替print()関数を作成してみるとよいでしょう。その代替print()関数では、出力するテキストをスキャンしてANSIエスケープシーケンスを探し、それを解析し、SetConsoleTextAttribute() APIを使用して該当する色設定を適用します。

 この記事では、さまざまな例を示して、Python 3.0の細かい変更点がデータ型、算術演算、文字列書式指定に及ぼす影響について説明しました。これ以外にも、Python 3.0では標準ライブラリに重要な変更が行われており、それについてはこのシリーズの次回の記事で説明します。

著者紹介

Gigi Sayfan(Gigi Sayfan)
 主に大規模分散システムを対象とした、C/C++/C#/Python/Javaによるクロスプラットフォームのオブジェクト指向プログラミングに精通。現在は、Numenta社で脳の働きを模した人工知能システムの開発に取り組んでいる。