インターナルPython ー Pythonの文字列とPyUnicodeObject構造体
前回の続きとなる.
インターナルPython ー PythonのオブジェクトとPyObject構造体の関係 - NO!と言えるようになりたい
今回は,UTF-8でエンコードされたバイト列から,文字列オブジェクトに変換するまでの,大まかな流れについて見てみる.
PyUnicodeObject構造体
Python 3.xから,文字列の型であるstr型は,Python 2.xで言うところのunicode型となっており,内部的にはUCS-2(もしくはUCS-4)で保持している.str型を表す構造体は,以下のように定義されている.
/* Include/unicodeobject.h */ 437 typedef struct { 438 PyObject_HEAD 439 Py_ssize_t length; /* Length of raw Unicode data in buffer */ 440 Py_UNICODE *str; /* Raw Unicode buffer */ 441 long hash; /* Hash value; -1 if not set */ 442 int state; /* != 0 if interned. In this case the two 443 * references from the dictionary to this object 444 * are *not* counted in ob_refcnt. */ 445 PyObject *defenc; /* (Default) Encoded version as Python 446 string, or NULL; this is used for 447 implementing the buffer protocol */ 448 } PyUnicodeObject;
PyObject_HEADはマクロとなっており,実態はPyObject構造体の変数を宣言しているだけである.
/* Include/object.h */ 80 /* PyObject_HEAD defines the initial segment of every PyObject. */ 81 #define PyObject_HEAD PyObject ob_base;
PyUnicodeObject構造体のインスタンス生成は,典型的にはPyUnicode_FromStringマクロを用いて行われる.例として,readlineモジュールを読み込んでいる部分を見てみる.readlineモジュールは,Pythonインタプリタ起動時に読み込まれ,該当箇所では次のようになっている.
/* Modules/main.c */ 510 PyObject *v; 511 v = PyImport_ImportModule("readline");
また,PyImport_ImportModule()関数は以下のように定義されている.
/* Python/import.c */ 2045 /* Import a module, either built-in, frozen, or external, and return 2046 its module object WITH INCREMENTED REFERENCE COUNT */ 2047 2048 PyObject * 2049 PyImport_ImportModule(const char *name) 2050 { 2051 PyObject *pname; 2052 PyObject *result; 2053 2054 pname = PyUnicode_FromString(name); 2055 if (pname == NULL) 2056 return NULL; 2057 result = PyImport_Import(pname); 2058 Py_DECREF(pname); 2059 return result; 2060 }
2054行目は,PyUnicode_FromString("readline")と呼び出されているに等しい.このPyUnicode_FromStringマクロでは,文字列オブジェクトを生成し,生成に失敗したらNULLポインタが返る.また,2058行目では必要なくなったオブジェクトの解放が,Py_DECREFマクロで行われている.
文字列オブジェクトの生成
では,文字列オブジェクトの確保についてを順追って見ていく.まずは,PyUnicode_FromString()関数の定義であるが,これは以下のようになっている.
556 PyObject *PyUnicode_FromString(const char *u) 557 { 558 size_t size = strlen(u); 559 if (size > PY_SSIZE_T_MAX) { 560 PyErr_SetString(PyExc_OverflowError, "input too long"); 561 return NULL; 562 } 563 564 return PyUnicode_FromStringAndSize(u, size); 565 } 566
引数のconst char *uは変換する文字列へのポインタであり,この文字列のエンコーディングはUTF-8でなければならない.
558行目で入力文字列のバイト長が計算され,559行目でそれがPY_SSIZE_T_MAXを超えていないかがチェックされている.もし超えていたなら,560行目でエラーコードのための文字列がセットされて,561行目でNULLポインタが返される.
なお,PY_SSIZE_T_MAXの定義は以下のようになっている.
/* Include/pyport.h */ 177 /* Largest positive value of type Py_ssize_t. */ 178 #define PY_SSIZE_T_MAX ((Py_ssize_t)(((size_t)-1)>>1))
-1は0xFFFFFFFFであり,これを1ビット右にシフトすると0x7FFFFFFFとなるので,つまりこれは,符号付きlongの最大値を表していることになる.
バイト長がPY_SSIZE_T_MAXを超えていなかったら,PyUnicode_FromStringAndSize()関数が呼ばれる.PyUnicode_FromStringAndSize()関数の定義は次のようになる.
/* Objects/unicodeobject.c */ 509 PyObject *PyUnicode_FromStringAndSize(const char *u, Py_ssize_t size) 510 { 511 PyUnicodeObject *unicode; 512 513 if (size < 0) { 514 PyErr_SetString(PyExc_SystemError, 515 "Negative size passed to PyUnicode_FromStringAndSize"); 516 return NULL; 517 } 518 519 /* If the Unicode data is known at construction time, we can apply 520 some optimizations which share commonly used objects. 521 Also, this means the input must be UTF-8, so fall back to the 522 UTF-8 decoder at the end. */ 523 if (u != NULL) { 524 525 /* Optimization for empty strings */ 526 if (size == 0 && unicode_empty != NULL) { 527 Py_INCREF(unicode_empty); 528 return (PyObject *)unicode_empty; 529 } 530 531 /* Single characters are shared when using this constructor. 532 Restrict to ASCII, since the input must be UTF-8. */ 533 if (size == 1 && Py_CHARMASK(*u) < 128) { 534 unicode = unicode_latin1[Py_CHARMASK(*u)]; 535 if (!unicode) { 536 unicode = _PyUnicode_New(1); 537 if (!unicode) 538 return NULL; 539 unicode->str[0] = Py_CHARMASK(*u); 540 unicode_latin1[Py_CHARMASK(*u)] = unicode; 541 } 542 Py_INCREF(unicode); 543 return (PyObject *)unicode; 544 } 545 546 return PyUnicode_DecodeUTF8(u, size, NULL); 547 } 548 549 unicode = _PyUnicode_New(size); 550 if (!unicode) 551 return NULL; 552 553 return (PyObject *)unicode; 554 }
513〜517行目では,バイト長を表す変数sizeが0より大きかをチェックしており,0未満であるならエラーを返している.文字列のオブジェクトの生成は,526行目以降のif文内で行っている.
から文字と,1文字ASCIIの扱い
Pythonでは効率化のため,から文字と,一文字のASCII文字を表す文字列オブジェクトのインスタンスは,一つずつしか生成されない.つまり,Pythonで以下のように文字列を作成しすると,実際に作成されるオブジェクトの総数はから文字が1つと,'a'が1つ,'aa'が2つとなる.
$ python >>> a, b = '', '' >>> c, d, e = 'a', 'a', 'a' >>> f, g = 'aa', 'aa'
Python内部では,から文字の文字列オブジェクトはunicode_emptyに,1文字ASCIIの文字列オブジェクトは,unicode_latin1に保存している.
unicode_emptyとunicode_latin1の宣言は以下の通りとなっている.
/* Objects/unicodeobject.c */ 110 /* The empty Unicode object is shared to improve performance. */ 111 static PyUnicodeObject *unicode_empty; 112 113 /* Single character Unicode strings in the Latin-1 range are being 114 shared as well. */ 115 static PyUnicodeObject *unicode_latin1[256];
また,これらの初期化は,_PyUnicode_Init()関数内で,以下のように行われている.
/* Objects/unicodeobject.c */ 9631 void _PyUnicode_Init(void) 9632 { 9633 int i; 9634 ... /* 省略 */ 9646 9647 /* Init the implementation */ 9648 free_list = NULL; 9649 numfree = 0; 9650 unicode_empty = _PyUnicode_New(0); 9651 if (!unicode_empty) 9652 return; 9653 9654 for (i = 0; i < 256; i++) 9655 unicode_latin1[i] = NULL; 9656 if (PyType_Ready(&PyUnicode_Type) < 0) 9657 Py_FatalError("Can't initialize 'unicode'"); 9658 ... /* 省略 */ 9665 } 9666
9650行目で,長さ0の文字列オブジェクトを生成してunicode_emptyに保存しており,9654 - 9655行目で,unicode_latin1[]配列をNULLで初期化していることが分かる.
先程のPythonプログラムを実行したあとの状態を絵で表すと,大体こんな感じになる.
ob_refcntはリファレンスカウントで,strは実際の文字列となる.
もう一度PyUnicode_FromStringAndSize()関数を見てみる.
/* Objects/unicodeobject.c */ 509 PyObject *PyUnicode_FromStringAndSize(const char *u, Py_ssize_t size) 510 { ... /* 省略 */ 525 /* Optimization for empty strings */ 526 if (size == 0 && unicode_empty != NULL) { 527 Py_INCREF(unicode_empty); 528 return (PyObject *)unicode_empty; 529 }
526行目では,バイト長が0かどうかがチェックされている.もし0であるなら,527行目でunicode_emptyのリファレンスカウントを増加させ,528行目でunicode_emptyを返り値として返している.
次に,入力が1文字のASCIIであった場合の処理部分を見てみる.
509 PyObject *PyUnicode_FromStringAndSize(const char *u, Py_ssize_t size) 510 { ... /* 省略 */ 531 /* Single characters are shared when using this constructor. 532 Restrict to ASCII, since the input must be UTF-8. */ 533 if (size == 1 && Py_CHARMASK(*u) < 128) { 534 unicode = unicode_latin1[Py_CHARMASK(*u)]; 535 if (!unicode) { 536 unicode = _PyUnicode_New(1); 537 if (!unicode) 538 return NULL; 539 unicode->str[0] = Py_CHARMASK(*u); 540 unicode_latin1[Py_CHARMASK(*u)] = unicode; 541 } 542 Py_INCREF(unicode); 543 return (PyObject *)unicode; 544 }
533行目では,入力文字が1バイトかつ,ASCIIであるかかどうかがチェックされる.Py_CHARMASKマクロはcharを符号無しの値に変換するマクロであり,定義は以下のようになっている.
/* Include/Python.h */ 132 /* Convert a possibly signed character to a nonnegative int */ 133 /* XXX This assumes characters are 8 bits wide */ 134 #ifdef __CHAR_UNSIGNED__ 135 #define Py_CHARMASK(c) (c) 136 #else 137 #define Py_CHARMASK(c) ((unsigned char)((c) & 0xff)) 138 #endif
UTF-8では,ASCII文字は128以下の値となるため,Py_CHARMASK(*u) < 128とすることで,ASCIIであるかをチェックできる.もしも入力が1文字のASCIIであったなら,if文内が実行されることになる.
既に述べたが,1文字ASCIIのオブジェクトは,unicode_latin1配列に格納される.つまり,unicode_latin1['a']が"a"という文字列のための文字列オブジェクトとなる.535行目以降では,オブジェクトが存在するかを確認し,存在しなかったら,536行目で新たにオブジェクトを生成し540行目で,生成したオブジェクトをunicode_latin1配列に格納している.もしも既に該当ASCII文字のオブジェクトが存在する場合は,単にリファレンスカウントをインクリメントして,それを返すだけである.
複数バイト文字列の扱い
最後に,入力バイトが1より大きかった場合と,入力文字列を指すポインタがNULLであった場合の処理を見てみる.
/* Objects/unicodeobject.c */ 509 PyObject *PyUnicode_FromStringAndSize(const char *u, Py_ssize_t size) 510 { ... /* 省略 */ 523 if (u != NULL) { ... /* 省略 */ 531 /* Single characters are shared when using this constructor. 532 Restrict to ASCII, since the input must be UTF-8. */ 533 if (size == 1 && Py_CHARMASK(*u) < 128) { ... /* 省略 */ 543 return (PyObject *)unicode; 544 } 545 546 return PyUnicode_DecodeUTF8(u, size, NULL); 547 } 548 549 unicode = _PyUnicode_New(size); 550 if (!unicode) 551 return NULL; 552 553 return (PyObject *)unicode; 554 }
546行目はバイト長が1より大きかった場合の処理であり,PyUnicode_DecodeUTF8マクロによりUTF-8からUCS-2へと文字コード変換された文字列オブジェクトが生成されて,それを返り値としている.
549行目以降では,sizeバイトの文字列オブジェクトを_PyUnicode_New()関数で生成して,それを返している.
今回のまとめ
文字列オブジェクト生成までの大体の流れはこんな感じになる.今回の重要な点は,から文字と,1文字のASCIIだけからなる文字列オブジェクトは,複数のインスタンスが生成されないということである.次は,_PyUnicode_New()と,PyUnicode_DecodeUTF8について見ていきたいと思う.