概要
Python では現在二つの整数(int)を区別している。通常の整数つまり短整
数は C の long の大きさ(典型的には 32 または 64 bit)に制限されてい
る。長整数は確保できるメモリにのみ制約を受ける。短整数に対する演算
結果が C long に収まらない場合、そこでエラーが起こる。その他にも違
いがいくつかある。この PEP はほとんどの解釈上の違いを無くし、これ
ら二つの型を Python ユーザーの視点からみて統一することを提案する。
理由
多くのプログラムは実際に動かしてみるとより大きな数の取り扱いが必要
であることに気付くが、後からアルゴリズムを変更するのは煩わしい。多
くの場合、必要性に関わらず長整数を使うと性能を損なう可能性がある。
言語に機械語長が現れることは移植性を損なう。たとえば、 32-bit 機と
64-bit 機の間ではこの理由によって Python のソースや .pyc をそのま
ま流用できない。
また、一般的な願望として、ほとんどの応用に無関係な不必要な詳細は
Python ユーザーから隠しておきたい。例としてはメモリーの確保が挙げ
られる。これは C では自分で書かないといけないが Python では自動的
にやってくれていて、大きさの判らない文字列やリストを使えるといった
利便性を与えてくれている。このような利便性を数の場合にも広げること
は意味のあることだ。
さらにこれにより新米 Python プログラマー(プログラミング自体の新米
かどうかに関わらず)が言語を使う前に学ぶべきことを一つ減らしてあげ
られるのだ。
実装法
当初、二つの別々の実装法が提案された(それぞれの著者が一つずつ)。
1. C long に対する PyInt 型のスロットを次のようにする。
union {
long i;
struct {
unsigned long length;
digit digits[1];
} bignum;
};
long の下位 n-1 bit だけが意味を持つ。最上位 bit は常にセットさ
れる。これで union を識別する。全ての PyInt 関数はどちらの型を
使うか決める前にこの bit を検査する。
2. 現在ある長短整数型は残し、そのかわり演算結果が短整数で表しきれ
なくなったら OverflowError を上げるかわりに長整数を返すことにす
る。新しい型 integer を抽象基底型として導入し、実装型 int と
long をその派生型にすることも考えられる。こうするとプログラム中
で整数性を調べる単独のテストを書けて便利である。
if isinstance(i, integer): ...
考慮の結果、二つ目の実装計画が選ばれた。それは、ずっと簡単に実装で
き、C API レベルで互換性を守り、さらに移行期間中部分的に実装してい
くことができるからである。
非互換性
以下に挙げる演算は長短整数に対し(通常は微妙に)異なる意味を持ち、ど
ちらか一方が幾分か変更されなければならない。このリストで全てを尽く
しているつもりである。もし他に同じ値でも長整数であるか短整数である
かによって違った結果になる演算を知っていたら、第2著者にメールして
欲しい。
- 現在のところ、短整数の << を除く全ての数値演算は短整数で表現でき
ない結果になると OverflowError を上げる。これからは長整数を返す
ように変える。以下に挙げる演算子が現在 OverflowError を上げてい
る。x+y、x-y、x*y、x**y、divmod(x, y)、x/y、x%y、-x。(最後の四
つは -sys.maxint-1 が絡んだ時にだけ桁溢れを起こす。)
- 現在のところ、短整数の <<n で bit がなくなることがある。今後はも
し bit を失いそうなときは全てがシフトされた長整数を返す。
- 現在のところ、短整数の8進と16進のリテラルは負の数を指定できる。
たとえば、32-bit 機では 0xffffffff == -1。しかし、これからは
0xffffffffL (2**32-1) と等しくなる。
- 現在のところ、'%u'と'%x'と'%o'の文字列フォーマット演算子と組み込
み関数の hex() と oct() は負の数に対して異なる振舞いをする。負の
短整数は unsigned C long として整形されるが、負の長整数は負符合
とともに整形される。今後はどちらの場合も現在の長整数と同様になる。
(ただし、現在 hex() と oct() がその出力を区別している末尾の L は
付かない) つまりどういうことかというと、'%u'が '%d'の異名になる
ということだ。そして、そのうち廃止されるだろう。
- 現在のところ、長整数の repr() は L で終わる文字列を返すが、短整
数の場合はこれが付かない。この L は付けられなくなる。
- 現在のところ、長整数の演算が短整数を返すことはない。これも変更さ
れる*かもしれない*、というのはそうなればある種の最適化が許される
からだ。
- 式 type(x).__name__ は短整数か長整数かに依っている。2番目の実装
が選ばれたので、この違いは今後も残る。
- 長短整数は marshal モジュールと pickle および cPickle モジュール
では異なった扱いを受ける。この違いは今後も残る。
- 小さい値(典型的には -1 以上 99 以下)の短整数は "intern" される
-- 結果がそのような値になる時はいつでも同じ値の既存の短整数が返
される。しかし同じ値であっても長整数はそうされない。この違いは今
後も残る。(intern するかどうかの保証は存在しないので、これを意味
的相違と呼ぶかどうかは議論の余地がある -- とはいえ is を短整数の
比較に使用していてこの intern のおかげで動いているコードがあるか
もしれない。そういったコードは長整数も使われると失敗するだろう。)
リテラル
整数リテラルの末尾に付けられる L は意味を失う。コンパイラーは値の
みに基づいて適切な型を選ぶ。
組み込み関数
関数 int() は短整数か長整数かを引数の値に応じて返すようになる。関
数 long() は int() を呼び出すことになる。組み込みの名前 'long' は
実装型としての long を表すために言語に残る。しかし、関数としては
int() を使うことを推奨する。というのは int() は必要な時には自動的
に長整数を返してくれるからだ。
C API
C API は変更されない。C コードでは相変わらず長短整数の違いを知って
いる必要がある。
PyArg_Parse*() API たちは既に長整数を、C int または long で表現で
きる範囲内ならば、受け入れている。だから、C int または long を引数
として受け取る関数は Python の long を扱うことを心配しなくて良い。
移行
移行には大きく二つの段階がある。
A. 短整数の演算で現在 OverflowError を上げているものは長整数を代わ
りに返すことになる。これがこの段階での唯一の変更点である。リテ
ラルは相変わらず短整数と長整数を分かつ。その他の上で挙げた意味
上の相違は(<< の振舞いも含めて)残る。それは、この段階はただ現在
OverflowError を上げている状況を変えるだけだからだ。この変更は
既存のコードを動かなくしないと仮定される。(この例外に依存してい
るようなコードは、考慮に入れるには入り組み過ぎている。) 極端に
過去との互換性に気を使う向きには、コマンドラインオプション(かま
たは warnings モジュールの呼び出し)がこの点に関する警告またはエ
ラーを出すようにしてくれるだろう。しかしこれはデフォルトでは切
られている。
B. 残りの意味的相違が処理される。ほとんどの場合、長整数の意味に合
わせられる。しかし、末尾の L は長整数の表現から除かれる。そのう
ち末尾に L の付いた整数リテラルに対するサポートも取り除かれる。
この作業はある種の古いコードが動かなくなるような過去との非互換
性をもたらすので、この段階は future 文と/か警告が必要になるだろ
うし、長い移行時間を要するだろう。
段階 A は Python 2.2 で実装される。
段階 B は Python 2.3 から実装されてゆく。予定としては次のような順
序を追うつもりでいる。
B1. 残りの意味的相違が処理される。以前と違う結果になる演算は警告を
出すようになる。これはデフォルト動作である。長整数の(末尾に L
の付いた)リテラルの使用に対する警告はコマンドラインオプション
で出せるようになるだろうが、デフォルトではない。
B2. デフォルトで長整数リテラルに警告を出すようになる。
B3. 以前と違う結果になる演算に対する警告はデフォルトでは出なくなる。
B4. 長整数リテラルはもはや使えなくなる。この問題に関連した警告はな
くなる。
我々は以下のような時間経過を提案する。
B1. Python 2.3.
B2. Python 2.4.
B3. 残りの Python 2.x 系列。
B4. Python 3.0 (少なくとも2年は先)
OverflowWarning
現在は OverflowError を上げている状況で出される警告の指針となる規
則を提示する。これは移行段階 A で適用される。
- 新しい警告カテゴリー OverflowWarning が導入される。これは組み込
みの名前である
- int が桁溢れを起こした場合、警告 OverflowWarning が出される。そ
のメッセージ引数で桁溢れを起こした演算が示される、たとえば
"integer addition" など。これにより sys.stderr に警告メッセージ
が出るかもしれないし、また例外が上げられるかもしれない。すべての
制御を握っているのは -W コマンドラインと warnings モジュールであ
る。
- 警告 OverflowWarning はデフォルトでは無視される。
- 警告 OverflowWarning は全ての警告と同様に -W コマンドラインオプ
ションを通してあるいは warnings.filterwarnings() 呼び出しを通し
て制御される。たとえば
python -Wdefault::OverflowWarning
は OverflowWarning をソースのある行で最初に起こった時に表示させ
る。あるいはまた
python -Werror::OverflowWarning
は OverflowWarning が起こった時はいつでも例外に変化させる。以下
のコードは警告をプログラム内部から有効にする。
import warnings
warnings.filterwarnings("default", "", OverflowWarning)
-W オプションについては python man page を、filterwarnings() に
ついては warnings モジュールの文書を参照のこと。
- 警告 OverflowWarning をエラーに変える時は OverflowError を使う。
これは過去との互換性のために必要である。
- 警告が例外に変えられない限りは演算(たとえば x+y)の結果は引数を長
整数に変換して再計算される。
例
もし長整数を整数引数の C 関数または組み込み演算に渡したとすると、
値がうまく当てはまる(いかに PyArg_ParseTuple() が実装されてるかに
よるが)かぎり短整数と同じに扱われる。値が大きすぎるときは、相変わ
らず OverflowError が上げられる。たとえば
def fact(n):
if n <= 1:
return 1
return n*fact(n-1)
A = "ABCDEFGHIJKLMNOPQ"
n = input("Gimme an int: ")
print A[fact(n)%17]
ここで n >= 13 とすると、今のところ OverflowError が上げられる(ユー
ザーが入力の末尾に L を入れない限りは)。計算されて出てくる添字は常
に range(17) の中に入っているというのに。新しいアプローチではこの
コードは正しく動いてくれる。添字は長整数として計算されるが、その値
は許される範囲に収まる。
解決された問題
これらの問題は以前は未解決だったが解決した。
- sys.maxint はどうするか? 残す。短整数と長整数を見分けるのに関係
するときは相変わらず関係するから(たとえばその値の型を調べる時)。
- 完全に'%u'を取り除くべきだろうか? 取り除くべし。
- << が整数を丸めないことを警告すべきだろうか? 警告すべし。
- 移植可能な最大の大きさを越えたら桁溢れの警告を出すべきだろうか?
否。
実装
段階 A の完全な実装が CVS にあり、Python 2.2a3 とともにリリースさ
れる。(Python 2.2a2 には組み入れない。) いまだ欠けているのは説明文
書とテストスイートである。
Copyright
This document has been placed in the public domain.
日本語訳について
日本語訳は Fe2+ (VED03370@nifty.ne.jp) が行ないました。
誤訳、誤植などありましたらお知らせ下さい。
元の文書にしたがって、自由な改変および再配布を認めます。