PEP 3141:数値の型階層
Python 3.0では、numbers
というモジュールが導入されました(Python 2.6にもあります)。このモジュールには、各数値型の抽象ベースクラス(ABC)が含まれています。これによって実質的に数値型の階層が形成され、各数値型は上位の数値型クラスのサブクラスになります。この階層は、SchemeのNumeric Towerをヒントにしています。Number
、Complex
、Real
、Rational
、Integral
の5種類のABCがあります。それぞれのクラス(Number
を除く)は前のクラスのサブクラスです。例えば、Integral
はRational
のサブタイプです。すべての整数Xは有理数X/1、つまり、Xを分子、1を分母とする有理数と見なすことができます。
ベースクラスNumber
は、実際のどの数値型とも対応していません。このクラスは、ある値が数値であるかどうかを検査する場合に備えて用意されています。通常、数値かどうかを調べるには、numbers.Complex
のサブクラスであることを確認できれば十分ですが、Number
クラスとComplex
クラスの間に、Complex
のサブクラスではない新しい数値を作成するようなまれなケースでは、Number
クラスが必要になります。
Complex
、Real
、Integral
のABCは、それぞれcomplex
型、floa
t型、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)
引数がint
かfloat
であることを明示的に検査しようとすると、あまり洗練されていないコードになります。有理数にも対応しようとすればなおさらです。
>>> 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準拠のクラスには大量のメソッドの実装が必要になるので、ここでは完全なコード例は示しません。