2019 5/18の #PyConK でのトークです
レガシーPython散策八雲アナグラ @AnaTofuZ 5/18 PyConKyushu 2019
View Slide
໌ͷʹཱͨͳ͍ࠓ͙͢ߦ͜͏ڭࣨ
self.inroduce()• 八雲アナグラ (id:AnaTofuZ)• 琉球大学修士1年• Okinawa.pm(沖縄Perlユーザーグループ)や Perl入学式in沖縄などに出没します• Pythonは殆ど書きませんが Python製のMercurialをよく使います
• 食後のお昼寝タイム• レガシーPythonを題材に 普段使っているPythonインタプリタが どのようにプログラミングされているかを探ります• C言語が出てきます
• Pythonとは• Pythonの歴史• Pythonの初期バージョンの特徴• C言語での実装に迫る
PythonCで書かれたCpython
Cに変換すれば早いやろ!!!な CythonPython
pythonで書かれたpython pypyPython
Javaで書かれたpython Jython(最近はcafebabepy)Python
python• 処理系が無数に乱立している事もあり スクリプト言語では用語の使い分けを行っている• 大文字の言語「Python」• プログラミング言語の仕様• 小文字の言語名「python」• 処理系の事(基本的にはCで書かれているもの)• それ以外の実装は実装名で呼ぶ
Python• 歴史的に一番古く、一番使われている Cpythonを見ていきます!!!• その為、皆さんがコマンドラインで よく利用している “python” の実装の話です
• Pythonとは• Pythonの歴史• Pythonの初期バージョン• 動かす• 実装を眺める• Python3の実装を少し見る
(C)Python• 1991年 0.9x• 最初に公開されたバージョン• 1994年 1.0• Python初期バージョン• 2000年 2.0• Haskellに強い影響を受け出す• 2008年 3.0• 文字列周りの仕様が大幅に変更される
LL• 1987年 Perl1.0リリース• 1988年 Perl2.0, Perl3.0• 1991年 2月 Python.0.9.0 3月 Perl4.0• 1993年 Ruby開発開始• 1994年 1月 Python1.0 10月 Perl5.0• 1995年 12月 Ruby
[ ] Perl Python• Perl5のオブジェクト指向では メソッドの第一引数がselfですが、 これはPythonを参考にして実装されているsub client {my $self = shift;return $self->{client};}
CPython• CPython内ではtagで0.97も打たれている➡ commit ログも見れる➡ Perlも同じようなリポジトリの運用• CPythonをcloneすると大体のバージョンの ソースコードがローカルで見る事が出来る
Python• 微妙にリポジトリに全てのバージョンが 残っていない• 手軽に試せるのは公式のwebpageから ダウンロードできる0.9.1 https://www.python.org/download/releases/early/
Python0.9.1• configureではなくMakefileがあり HighSierraだと普通にmakeできる• 検索するといくつか触っている人がいるので 幾つかのブログで紹介されている• 公式ドキュメントを参照する場合は リポジトリに含まれているものを利用する
Python0.9.1• manページ• TeXで書かれたドキュメント• コアライブラリ• testall.py
Python0.9.1• manページ➡ ほとんど役に立たない• TeXで書かれたドキュメント➡ 結構書かれている• コアライブラリ➡ 複雑なプログラミングの参考になる• testall.py➡ 使い方の参考になる
Python• LaTeXで書かれている (書き方が古いらしい)• Makefileがあり、makeを実行すると xdvi (x window のdviビューワー)が起動する• xdviの機能でPostScriptに出力する事が可能• 全23ページ
Python0.9.1• あるもの• Intなどのデータ型• リスト、dict• dictは開発中で、構文には組み込まれていない• オブジェクトシステム、継承• 今のPythonとはかなり異なる• import機能• コアライブラリがついていた
Python0.9.1• ないもの• タプル• map, lambda, filter• version1からLispの影響で導入• リスト内包記法• version2からHaskellの影響で導入• ジェネレーター, withなど
コードを書く (我々)字句解析構⽂解析バイナリファイル⽣成バイナリファイルを実⾏
Pythonコードを書く (我々)字句解析構⽂解析 バイトコード⽣成バイトコードをVMが実⾏
Python0.9.1• __init__ が存在しない• createメソッドを手で書く世界観• クラスの継承は =で書くfrom Split import Splitclass HVSplit() = Split():#def create(self, (parent, hv)):# hv is 0 or 1 for HSplit or VSplitself = Split.create(self, parent)self.hv = hvreturn self
Python0.9.1• dicやタプルなど、実装しているが構文に組み込まれておらず使えない機能が多い• 文字列は “hoge” ではだめで ‘hoge’ なシングルクォートしか使えない
PythonୈҰ෦ऴ───੍࡞ɾஶ࡞N○K
$ݴޠͰͷ࣮ʹഭ͍͖ͬͯ·͢
• Pythonの処理の流れ• コードのおおよその雰囲気• オブジェクト構造体• 0.9.1のバグを直す• 計算について
Python• ソースコードをバイトコード化し VMが実行する流れは0.9.1から3まで同じ• 最適化やAPI、計算方法などには違いがあるが 大まかな枠組みは既に形成されている• バイトコードを確認するdisモジュールが 既に0.9.1から付随している
CPython• Grammarファイルに記述されている• EBNF(拡張バッカスナウア記法)っぽいもので定義• PythonはMake時に、このファイルから 決定性有限オートマトンを自動生成し、それを利用する• Python公式の0.971は既にGrammarが ビルドされている為、自動生成はされない
Python Grammar• なんとなく読める気がする…?• この結果構文木を作る ( a+b の例 )•funcdef: 'def' NAME parameters ':' suiteparameters: '(' [fplist] ')'fplist: fpdef (',' fpdef)*fpdef: NAME | '(' fplist ')'(expr: (term: (NAME: a)), (OP: +), (term: (NAME: b)) )
• 構文解析の結果から扱いやすい バイトコード形式に内部的に変換する ➡ 速度向上と実装の分離の為• 生成したバイトコードは、計算機をエミュレートするプロセスVMで実行される
PythonVM• スタックマシンと呼ばれるVMの作り方• 大きいスタックで値をやりとりする• RubyのYARVも同じ方式• 実際のCPUに似せている方式はレジスタマシン• Perl6はこの方式
• バイトコードは実行する命令(オペコード)と 引数(オペランド)や、データの集まり• オペコードは数値に変換される• CPUのプログラムカウンタ的に、バイトコードを進めながらプログラムを実行していく
PythonΦϖίʔυಡΈࠐΈ໋ྩ࣮ߦ ҾಡΈࠐΈ
PythonVMfor (;;) {static ticker;/* Extract opcode and argument */opcode = NEXTOP();if (HAS_ARG(opcode))oparg = NEXTARG();/* Main switch on opcode */switch (opcode) {case BINARY_ADD:w = POP();v = POP();x = add(v, w);DECREF(v);DECREF(w);PUSH(x);break;ΦϖίʔυΛऔΓग़͠
PythonVMfor (;;) {static ticker;/* Extract opcode and argument */opcode = NEXTOP();if (HAS_ARG(opcode))oparg = NEXTARG();/* Main switch on opcode */switch (opcode) {case BINARY_ADD:w = POP();v = POP();x = add(v, w);DECREF(v);DECREF(w);PUSH(x);break;Ҿ͕͋ΕҾΛऔΓग़͠
PythonVMfor (;;) {static ticker;/* Extract opcode and argument */opcode = NEXTOP();if (HAS_ARG(opcode))oparg = NEXTARG();/* Main switch on opcode */switch (opcode) {case BINARY_ADD:w = POP();v = POP();x = add(v, w);DECREF(v);DECREF(w);PUSH(x);break;TXJUDIจͰ໋ྩͷॲཧʹذ
PythonVMcase BINARY_ADD:w = POP();v = POP();x = add(v, w);DECREF(v);DECREF(w);PUSH(x);break;ελοΫ͔ΒͭͷมΛऔΓग़͠wv
PythonVMcase BINARY_ADD:w = POP();v = POP();x = add(v, w);DECREF(v);DECREF(w);PUSH(x);break;ͭΛͯ͠YΛ࡞
PythonVMcase BINARY_ADD:w = POP();v = POP();x = add(v, w);DECREF(v);DECREF(w);PUSH(x);break;WͱXͷϦϑΝϨϯεΧϯτΛσΫϦϝϯτ͢Δ
PythonVMcase BINARY_ADD:w = POP();v = POP();x = add(v, w);DECREF(v);DECREF(w);PUSH(x);break;YΛελοΫʹ͍Εͯऴྃx
Python3• Python3では、いちいちループすると遅いので GCCの拡張機能のラベルgotoなどを利用し スピードの高速化を図っている• 他のスクリプト言語も大体同じような実装なので、興味があれば他の言語も見てみましょう
Python mainmain(argc, argv)int argc;char **argv;{char *filename = NULL;FILE *fp = stdin;initargs(&argc, &argv);if (argc > 1 && strcmp(argv[1], "-") != 0)filename = argv[1];if (filename != NULL) {if ((fp = fopen(filename, "r")) == NULL) {fprintf(stderr, "python: can't open file '%s'\n",filename);exit(2);}}initall();setpythonpath(getpythonpath());setpythonargv(argc-1, argv+1);goaway(run(fp, filename == NULL ? "" : filename));/*NOTREACHED*/}
Python mainmain(argc, argv)int argc;char **argv;{char *filename = NULL;FILE *fp = stdin;initargs(&argc, &argv);if (argc > 1 && strcmp(argv[1], "-") != 0)filename = argv[1];,3$Ͱؔએݴ
Python mainmain(argc, argv)int argc;char **argv;{char *filename = NULL;FILE *fp = stdin;initargs(&argc, &argv);if (argc > 1 && strcmp(argv[1], "-") != 0)filename = argv[1];ҾΛϑΝΠϧ໊ʹઃఆ
Python maininitall();setpythonpath(getpythonpath());setpythonargv(argc-1, argv+1);goaway(run(fp, filename == NULL ? "" : filename)/*NOTREACHED*/࣮ଶSVOͰHPBXBZͰΤϥʔͷย͚
Python• Pythonでは様々なオブジェクトが登場します• C言語の世界では、これらオブジェクトは 主に構造体として定義しています (構造体 = 複数の型と名前を持つもの)• Python3では基本的にPyObjectと呼ばれるものから派生します
Python• メモリのヒープ領域に必ず置かれる• スタックや静的領域には置かれない• その為メモリの中でオブジェクトの アドレスが変更される事は無いCODEDATACONST DATA...HEAP...STACK...
object• python0.9.1ではobject構造体がベース• OB_HEADをメンバとして持つ構造体• OB_HEADは二種類の変数定義のマクロ#define OB_HEAD \unsigned int ob_refcnt; \struct _typeobject *ob_type;typedef struct _object {OB_HEAD} object;
varobject• objectを拡張したvarobject構造体もある• PyVarObjectに対応する• サイズの概念が追加される#define OB_VARHEAD \OB_HEAD \unsigned int ob_size; /* Number of items invariable part */typedef struct {OB_VARHEAD} varobject;
objectob_refcntob_type
objectob_refcntob_typeΨϕʔδίϨΫλϦϑΝϨϯεΧϯτͷΧϯλʔͲ͔͜Βࢀর͞Εͳ͘ͳΔ ʹͳΔͱ ղ์͞ΕΔ
GC• マクロDECREFでGC管理をしている• ob_refcntが0の場合 DELREFによりtp_deallocで解放#define DECREF(op) \if (--ref_total, --(op)->ob_refcnt > 0) \; \else \DELREF(op)#define DELREF(op) \ (*(op)->ob_type->tp_dealloc)((object *)(op))
objectob_refcntob_typeΦϒδΣΫτͷܕใͷϙΠϯλ
typedef struct _typeobject {OB_VARHEADchar *tp_name; /* For printing */unsigned int tp_basicsize, tp_itemsize; /* Forallocation *//* Methods to implement standard operations */void (*tp_dealloc) FPROTO((object *));void (*tp_print) FPROTO((object *, FILE *, int));object *(*tp_getattr) FPROTO((object *, char *));int (*tp_setattr) FPROTO((object *, char *, objectint (*tp_compare) FPROTO((object *, object *));object *(*tp_repr) FPROTO((object *));/* Method suites for standard classes */number_methods *tp_as_number;sequence_methods *tp_as_sequence;mapping_methods *tp_as_mapping;} typeobject;
typeobjecttypedef struct _typeobject {OB_VARHEADchar *tp_name; /* For printing */unsigned int tp_basicsize, tp_itemsize; /* Forallocation *//* Methods to implement standard operations */void (*tp_dealloc) FPROTO((object *));void (*tp_print) FPROTO((object *, FILE *, int));object *(*tp_getattr) FPROTO((object *, char *));int (*tp_setattr) FPROTO((object *, char *, objecint (*tp_compare) FPROTO((object *, object *));object *(*tp_repr) FPROTO((object *));ܕͷ໊લใ
typeobjecttypedef struct _typeobject {OB_VARHEADchar *tp_name; /* For printing */unsigned int tp_basicsize, tp_itemsize; /* Forallocation *//* Methods to implement standard operations */void (*tp_dealloc) FPROTO((object *));void (*tp_print) FPROTO((object *, FILE *, int));object *(*tp_getattr) FPROTO((object *, char *));int (*tp_setattr) FPROTO((object *, char *, objecint (*tp_compare) FPROTO((object *, object *));object *(*tp_repr) FPROTO((object *));ΦϒδΣΫτͷղ์ʹར༻ͨؔ͠
typeobject• void* なこれは、関数ポインタ• 関数の参照を代入する事が出来る• 構造体に関数ポインタをいれることで インターフェース的な機能を実現• FPROTOはマクロで、プロトタイプ宣言を抑制• 当時のコンパイラはプロトタイプ宣言があると 解釈出来ないケースもあり、マクロで抑制 (プロトタイプ宣言…Cで関数定義時に必要)void (*tp_dealloc) FPROTO((object *));
typeobjecttypeobject Inttype = {OB_HEAD_INIT(&Typetype)0,"int",sizeof(intobject),0,int_dealloc, /*tp_dealloc*/};typeobject Floattype = {OB_HEAD_INIT(&Typetype)0,"float",sizeof(floatobject),0,free, /*tp_dealloc*/};
typeobjecttypeobject Inttype = {OB_HEAD_INIT(&Typetype)0,"int",sizeof(intobject),0,int_dealloc, /*tp_dealloc*/};typeobject Floattype = {OB_HEAD_INIT(&Typetype)0,"float",sizeof(floatobject),0,free, /*tp_dealloc*/};qPBUͰGSFFJOUͰJOU@EFBMMPD͕UQ@EFBMMPDʹొΦϒδΣΫτ͝ͱʹҟͳΔؔΛಉ͡ΠϯλʔϑΣʔεͰݺͼग़͕͠Մೳ
typeobjecttypedef struct _typeobject {object *(*tp_repr) FPROTO((object *));/* Method suites for standard classes */number_methods *tp_as_number;sequence_methods *tp_as_sequence;mapping_methods *tp_as_mapping;} typeobject;JOU qPBUͷܭࢉͰར༻͢Δؔू߹ͷ ߏମͷࢀর
number_methods• number_methosも四則演算などで使うメソッドをデータ型によって変更可能にしているstatic number_methods int_as_number = {int_add, /*tp_add*/int_sub, /*tp_subtract*/int_mul, /*tp_multiply*/int_div, /*tp_divide*/int_rem, /*tp_remainder*/int_pow, /*tp_power*/int_neg, /*tp_negate*/int_pos, /*tp_plus*/};
( )object• python0.9.1ではobject構造体がベース• OB_HEADをメンバとして持つ構造体• OB_HEADは二種類の変数定義のマクロ#define OB_HEAD \unsigned int ob_refcnt; \struct _typeobject *ob_type;typedef struct _object {OB_HEAD} object;
Python3 PyObject• 殆ど構造が同じtypedef struct _object {_PyObject_HEAD_EXTRAPy_ssize_t ob_refcnt;struct _typeobject *ob_type;} PyObject;#define PyObject_VAR_HEAD PyVarObject ob_base;
0.9.1 IntObject• intと言いつつ実際に値が入るob_ivalは long型•typedef struct {OB_HEADlong ob_ival;} intobject;extern typeobject Inttype;#define is_intobject(op) ((op)->ob_type == &Inttype)
Python3 Int• Python3ではlongobjectという名前• 大体同じだが、digitの要素1の配列を持つ• Cで構造体のメンバに 可変長配列を設定するテクニックstruct _longobject {PyObject_VAR_HEADdigit ob_digit[1];};
typedef struct {OB_VARHEADobject **ob_item;} listobject;extern typeobject Listtype;#define is_listobject(op) ((op)->ob_type == &Listtype)• リストの構造体
typedef struct {OB_VARHEADobject **ob_item;} listobject;extern typeobject Listtype;#define is_listobject(op) ((op)->ob_type == &Listtype)ΦϒδΣΫτͷࢀরྻϙΠϯλͷϙΠϯλ• ob_itemはC言語の配列になるので O(1)でアクセス可能
?• 基本的にこれらの構造体はポインタでcpythonの内部ではやりとりされる (ヒープ領域に常に確保されている)• 構造体はそれぞれ型が異なるが いちいち管理するとスタックの扱いがやっかい
IntObject• objectとintObjectの違いはob_ivalの有無typedef struct {OB_HEADlong ob_ival;} intobject;typedef struct _object {OB_HEAD} object;
IntObjecttypedef struct {OB_HEADlong ob_ival;} intobject;͜͏ݟΔͱobject
IntObjecttypedef struct {OB_HEADlong ob_ival;} intobject;͜͏ݟΔͱobject͜͏ݟΔͱintobject
?• 必ず構造体の一番上にOB_HEADがあることで objectのポインタにキャストする事が出来る
>>> 50 + 60Process 79910 stoppedframe #2: 0x00000001000036b9 python`eval_codeat cev839 case BINARY_ADD:840 w = POP();841 v = POP();-> 842 x = add(v, w);843 DECREF(v);844 DECREF(w);845 PUSH(x);
>>> 50 + 60(lldb) p v(object *) $0 = 0x00000001002007c0(lldb) p *v(object) $4 = {ob_refcnt = 2ob_type = 0x0000000100020848}(lldb) p *(intobject *)v(intobject) $3 = {ob_refcnt = 2ob_type = 0x0000000100020848ob_ival = 50}object型として扱われているintobjectにキャストすると値が⾒える!!
Python0.9.1• Python0.9.1ではテストの掛け算の部分で死ぬ+mumei+anatofuz$ src/python>>> print 1*1Unhandled exception: run-time error: integer overflowStack backtrace (innermost last):File "", line 1
Python0.9.1• Python0.9.1ではテストの掛け算の部分で死ぬ+mumei+anatofuz$ src/python>>> print 1*1Unhandled exception: run-time error: integer overflowStack backtrace (innermost last):File "", line 1_⼈⼈⼈⼈⼈⼈⼈⼈⼈⼈⼈⼈_> 1*1でオーバーフロー!! < ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^ ̄
Python0.9.1• grep すると src/intobject.cの中にあるらしい• intobjectはPython0.9.1で使われる 基本のオブジェクト+mumei+anatofuz$ grep 'integer overflow' src/*.csrc/intobject.c:err_setstr(OverflowError, "integer overflow");
Python0.9.1static object *err_ovf(){err_setstr(OverflowError, "integer overflow");return NULL;}• エラーを文字列として出すだけの関数• これが呼び出されている部分を探す➡ intオブジェクトの四則演算で呼びだれている
int_mulstatic object *int_mul(v, w)intobject *v;register object *w;{register long a, b;double x;if (!is_intobject(w)) {err_badarg();return NULL;}a = v->ob_ival;b = ((intobject *)w) -> ob_ival;x = (double)a * (double)b;if (x > 0x7fffffff || x < (double) (long) 0x80000000)return err_ovf();return newintobject(a * b);}
static object *int_mul(v, w)intobject *v;register object *w;{register long a, b;double x;if (!is_intobject(w)) {err_badarg();return NULL;}a = v->ob_ival;b = ((intobject *)w) -> ob_ival;x = (double)a * (double)b;if (x > 0x7fffffff || x < (double) (long) 0x80000000)return err_ovf();return newintobject(a * b);}X͕JOUͱͯ͠ܭࢉग़དྷΔ͔Λఆ͢Δint_mul
static object *int_mul(v, w)intobject *v;register object *w;{register long a, b;double x;if (!is_intobject(w)) {err_badarg();return NULL;}a = v->ob_ival;b = ((intobject *)w) -> ob_ival;x = (double)a * (double)b;if (x > 0x7fffffff || x < (double) (long) 0x80000000)return err_ovf();return newintobject(a * b);}ΦϒδΣΫτͅɺ͔͆ΒΛB Cʹೖint_mul
static object *int_mul(v, w)intobject *v;register object *w;{register long a, b;double x;if (!is_intobject(w)) {err_badarg();return NULL;}a = v->ob_ival;b = ((intobject *)w) -> ob_ival;x = (double)a * (double)b;if (x > 0x7fffffff || x < (double) (long) 0x80000000)return err_ovf();return newintobject(a * b);}֬ೝͷͨΊʹֻ͚ࢉΛ࣮ߦ Θ͔Γ͍͢int_mul
static object *int_mul(v, w)intobject *v;register object *w;{register long a, b;double x;if (!is_intobject(w)) {err_badarg();return NULL;}a = v->ob_ival;b = ((intobject *)w) -> ob_ival;x = (double)a * (double)b;if (x > 0x7fffffff || x < (double) (long) 0x80000000)return err_ovf();return newintobject(a * b);}int_mul
static object *int_mul(v, w)intobject *v;register object *w;{register long a, b;double x;if (!is_intobject(w)) {err_badarg();return NULL;}a = v->ob_ival;b = ((intobject *)w) -> ob_ival;x = (double)a * (double)b;if (x > 0x7fffffff || x < (double) (long) 0x80000000)return err_ovf();return newintobject(a * b);}JOUܕͰܭࢉͨ݁͠Ռ͔Β৽͍͠ΦϒδΣΫτΛฦ٫int_mul
Python0.9.1x = (double)a * (double)b;if (x > 0x7fffffff || x < (double) (long) 0x80000000)return err_ovf();• 掛け算の結果がintの範囲内に収まっていないと err_ovf(オーバーフローを示す例外)の 関数を返す
Python0.9.1x = (double)a * (double)b;if (x > 0x7fffffff || x < (double) (long) 0x80000000)return err_ovf();• 0x7fffffffが32bitのintの最大値 (2^31 -1)
Python0.9.1x = (double)a * (double)b;if (x > 0x7fffffff || x < (double) (long) 0x80000000)return err_ovf();• 0x7fffffffが32bitのintの最大値 (2^31 -1)• 0x80000000が32bitのintの最小値
Python0.9.1x = (double)a * (double)b;if (x > 0x7fffffff || x < (double) (long) 0x80000000)return err_ovf();• 0x7fffffffが32bitのintの最大値 (2^31 -1)• 0x80000000が32bitのintの最小値• -2^31が正解• バグの原因!!!!!
Python0.9.1• C言語でプリミティブ型の最大値を見る際は 型キャストの問題で マクロを利用するのが望ましい• limits.hというヘッダーにINT_MAXとINT_MINがマクロとして定義されているので使用する
Python0.9.1• 適切にマクロを利用して範囲を見る事で バグを修正#include …(中略)…x = (double)a * (double)b;if (x > INT_MAX || x < INT_MIN)return err_ovf();
Python0.9.1• 掛け算が出来るように!!!!!!!!!+mumei+anatofuz$ src/python>>> print 4*5*-1-20
Python3• 基本的にはPython0.9.1と同じコンセプト• 関数ポインタが利用される• オブジェクトごとの掛け算の実装• オブジェクト2つ取り出して掛け算• 本当に? と言われそうなので見ていきます
Python3 (VM)• POPとTOPで値を取り出し、関数に渡すcase TARGET(BINARY_MULTIPLY): {PyObject *right = POP();PyObject *left = TOP();PyObject *res = PyNumber_Multiply(left, right);Py_DECREF(left);Py_DECREF(right);SET_TOP(res);if (res == NULL)goto error;DISPATCH();}
Python3 ( )• binary_op1でスロットに登録された演算を実行 (実際はもう少し色々している)PyObject *PyNumber_Multiply(PyObject *v, PyObject *w){PyObject *result =binary_op1(v, w, NB_SLOT(nb_multiply));return result;}
static PyObject *binary_op1(PyObject *v, PyObject *w, const int op_slot){PyObject *x;binaryfunc slotv = NULL;binaryfunc slotw = NULL;if (v->ob_type->tp_as_number != NULL)slotv = NB_BINOP(v->ob_type->tp_as_number, op_slot);if (slotv) {if (slotw && PyType_IsSubtype( w->ob_type, v->ob_type)) {x = slotw(v, w);if (x != Py_NotImplemented)return x;
static PyObject *binary_op1(PyObject *v, PyObject *w, const int op_slot){PyObject *x;binaryfunc slotv = NULL;binaryfunc slotw = NULL;if (v->ob_type->tp_as_number != NULL)slotv = NB_BINOP(v->ob_type->tp_as_number, op_slot);if (slotv) {if (slotw && PyType_IsSubtype( w->ob_type, v->ob_type)) {x = slotw(v, w);if (x != Py_NotImplemented)return x;ֻ͚ࢉͳͲʹରԠͨ͠൪߸͕ೖ͍ͬͯΔ
static PyObject *binary_op1(PyObject *v, PyObject *w, const int op_slot){PyObject *x;binaryfunc slotv = NULL;binaryfunc slotw = NULL;if (v->ob_type->tp_as_number != NULL)slotv = NB_BINOP(v->ob_type->tp_as_number, op_slot);if (slotv) {if (slotw && PyType_IsSubtype( w->ob_type, v->ob_type)) {x = slotw(v, w);if (x != Py_NotImplemented)return x;TMPUWม͚ͩͲʜ
static PyObject *binary_op1(PyObject *v, PyObject *w, const int op_slot){PyObject *x;binaryfunc slotv = NULL;binaryfunc slotw = NULL;if (v->ob_type->tp_as_number != NULL)slotv = NB_BINOP(v->ob_type->tp_as_number, op_slot);if (slotv) {if (slotw && PyType_IsSubtype( w->ob_type, v->ob_type)) {x = slotw(v, w);if (x != Py_NotImplemented)return x;ؔΈ͍ͨʹݺͼग़͍ͯ͠Δ
• そういえばPython0.9.1はinterface的に オブジェクトごと関数をポインタに 設定していましたstatic number_methods int_as_number = {int_add, /*tp_add*/int_sub, /*tp_subtract*/int_mul, /*tp_multiply*/int_div, /*tp_divide*/int_rem, /*tp_remainder*/int_pow, /*tp_power*/int_neg, /*tp_negate*/int_pos, /*tp_plus*/};
• NB_BINOPは配列に設定されている計算用の 関数を取り出している (指定をslot番号で行う)#define NB_BINOP(nb_methods, slot) \(*(binaryfunc*)(& ((char*)nb_methods)[slot]))
• 0.9.1と同じようにいろいろな関数を設定!!static PyNumberMethods long_as_number = {(binaryfunc)long_add, /*nb_add*/(binaryfunc)long_sub, /*nb_subtract*/(binaryfunc)long_mul, /*nb_multiply*/long_mod, /*nb_remainder*/long_divmod, /*nb_divmod*/long_pow, /*nb_power*/(unaryfunc)long_neg, /*nb_negative*/long_long, /*tp_positive*/(unaryfunc)long_abs, /*tp_absolute*/(inquiry)long_bool, /*tp_bool*/
• Python0.9.1と似たような処理!!!!static PyObject *long_mul(PyLongObject *a, PyLongObject *b){PyLongObject *z;CHECK_BINOP(a, b);/* fast path for single-digit multiplication */if (Py_ABS(Py_SIZE(a)) <= 1 && Py_ABS(Py_SIZE(b))<= 1) {stwodigits v = (stwodigits)(MEDIUM_VALUE(a)) * MEDIUM_VALUE(b);return PyLong_FromLongLong((long long)v);}}
• Pythonのオブジェクトは大体objectを継承• キャストして使い、ポインタで基本やりとり• Pythonはバイトコードの形に変換される• 実行される命令はオブジェクトによって異なる• interface的に関数ポインタで制御• 大まかな処理はレガシーも同じ!
( )• Pythonの歴史にある程度詳しくなりました• Pythonインタプリタがどの様に書かれているか ちょっと見てみました• Pytonインタプリタも、結局はCのプログラムなので、読解してみて自分だけのPythonに改造してみよう!!
• 基本的に正の数で表現されている為 負の数は、SIZEを見てよしなに表現する/* convert a PyLong of size 1, 0 or -1 to an sdigit */#define MEDIUM_VALUE(x) (assert(-1 <= Py_SIZE(x) &&Py_SIZE(x) <= 1), \Py_SIZE(x) < 0 ? -(sdigit)(x)->ob_digit[0] :\(Py_SIZE(x) == 0 ? (sdigit)0 :\(sdigit)(x)->ob_digit[0]))
case PRINT_ITEM:v = POP();fp = sysgetfile("stdout", stdout);if (needspace)fprintf(fp, " ");if (is_stringobject(v)) {char *s = getstringvalue(v);int len = getstringsize(v);fwrite(s, 1, len, fp);if (len > 0 && s[len-1] == '\n')needspace = 0;elseneedspace = 1;}else {printobject(v, fp, 0);needspace = 1;}DECREF(v);break;13*/5@*5&. QSJOUͷΦϖίʔυ
case PRINT_ITEM:v = POP();fp = sysgetfile("stdout", stdout);if (needspace)fprintf(fp, " ");if (is_stringobject(v)) {char *s = getstringvalue(v);int len = getstringsize(v);fwrite(s, 1, len, fp);if (len > 0 && s[len-1] == '\n')needspace = 0;elseneedspace = 1;}else {printobject(v, fp, 0);needspace = 1;}DECREF(v);break;GXSJUFͰTUEPVUඪ४ग़ྗʹॻ͖ࠐΈ
print• Python0.9.1の出力は素朴にfwrite• Python3ではバッファなども考慮している為 こんなにシンプルに実装していない(lldb) p len(int) $0 = 4(lldb) p s(char *) $1 = 0x00000001002016a4 "hoge"char *s = getstringvalue(v);int len = getstringsize(v);fwrite(s, 1, len, fp);