Upgrade to Pro — share decks privately, control downloads, hide ads and more …

レガシーPython散策

 レガシーPython散策

2019 5/18の #PyConK  でのトークです

AnaTofuZ

May 18, 2019
Tweet

More Decks by AnaTofuZ

Other Decks in Programming

Transcript

  1. (C)Python • 1991年 0.9x • 最初に公開されたバージョン • 1994年 1.0 • Python初期バージョン

    • 2000年 2.0 • Haskellに強い影響を受け出す • 2008年 3.0 • 文字列周りの仕様が大幅に変更される
  2. 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
  3. Python0.9.1 • manページ ➡ ほとんど役に立たない • TeXで書かれたドキュメント ➡ 結構書かれている •

    コアライブラリ ➡ 複雑なプログラミングの参考になる • testall.py ➡ 使い方の参考になる
  4. Python0.9.1 • あるもの • Intなどのデータ型 • リスト、dict • dictは開発中で、構文には組み込まれていない •

    オブジェクトシステム、継承 • 今のPythonとはかなり異なる • import機能 • コアライブラリがついていた
  5. Python0.9.1 • ないもの • タプル • map, lambda, filter •

    version1からLispの影響で導入 • リスト内包記法 • version2からHaskellの影響で導入 • ジェネレーター, withなど
  6. Python0.9.1 • __init__ が存在しない • createメソッドを手で書く世界観 • クラスの継承は =で書く from

    Split import Split class HVSplit() = Split(): # def create(self, (parent, hv)): # hv is 0 or 1 for HSplit or VSplit self = Split.create(self, parent) self.hv = hv return self
  7. Python Grammar • なんとなく読める気がする…? • この結果構文木を作る ( a+b の例 )

    • funcdef: 'def' NAME parameters ':' suite parameters: '(' [fplist] ')' fplist: fpdef (',' fpdef)* fpdef: NAME | '(' fplist ')' (expr: 
 (term: (NAME: a)), (OP: +), (term: (NAME: b))
 )
  8. PythonVM for (;;) { 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; ΦϖίʔυΛ औΓग़͠
  9. PythonVM for (;;) { 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; Ҿ਺͕͋Ε͹Ҿ਺Λ औΓग़͠
  10. PythonVM for (;;) { 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จͰ ໋ྩͷॲཧʹ෼ذ
  11. PythonVM case BINARY_ADD: w = POP(); v = POP(); x

    = add(v, w); DECREF(v); DECREF(w); PUSH(x); break; ελοΫ͔Β ͭͷม਺ΛऔΓग़͠ w v
  12. PythonVM case BINARY_ADD: w = POP(); v = POP(); x

    = add(v, w); DECREF(v); DECREF(w); PUSH(x); break; ͭΛ଍ͯ͠YΛ࡞੒
  13. PythonVM case BINARY_ADD: w = POP(); v = POP(); x

    = add(v, w); DECREF(v); DECREF(w); PUSH(x); break; WͱXͷϦϑΝϨϯε Χ΢ϯτΛ σΫϦϝϯτ͢Δ
  14. PythonVM case BINARY_ADD: w = POP(); v = POP(); x

    = add(v, w); DECREF(v); DECREF(w); PUSH(x); break; YΛελοΫʹ͍Εͯऴྃ x
  15. Python main main(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 ? "<stdin>" : filename)); /*NOTREACHED*/ }
  16. Python main main(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$Ͱؔ਺એݴ
  17. Python main main(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]; Ҿ਺ΛϑΝΠϧ໊ʹ ઃఆ
  18. Python main initall(); setpythonpath(getpythonpath()); setpythonargv(argc-1, argv+1); goaway(run(fp, filename == NULL

    ? "<stdin>" : filename) /*NOTREACHED*/ ࣮ଶ͸SVOͰ HPBXBZͰΤϥʔͷ ย෇͚
  19. varobject • objectを拡張したvarobject構造体もある • PyVarObjectに対応する • サイズの概念が追加される #define OB_VARHEAD \

    OB_HEAD \ unsigned int ob_size; /* Number of items in variable part */ typedef struct { OB_VARHEAD } varobject;
  20. 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))
  21. typedef struct _typeobject { OB_VARHEAD char *tp_name; /* For printing

    */ unsigned int tp_basicsize, tp_itemsize; /* For allocation */ /* 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 *, object int (*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;
  22. typeobject typedef struct _typeobject { OB_VARHEAD char *tp_name; /* For

    printing */ unsigned int tp_basicsize, tp_itemsize; /* For allocation */ /* 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 *, objec int (*tp_compare) FPROTO((object *, object *)); object *(*tp_repr) FPROTO((object *)); ܕͷ໊લ৘ใ
  23. typeobject typedef struct _typeobject { OB_VARHEAD char *tp_name; /* For

    printing */ unsigned int tp_basicsize, tp_itemsize; /* For allocation */ /* 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 *, objec int (*tp_compare) FPROTO((object *, object *)); object *(*tp_repr) FPROTO((object *)); ΦϒδΣΫτͷղ์ʹ ར༻ͨؔ͠਺
  24. typeobject • void* なこれは、関数ポインタ • 関数の参照を代入する事が出来る • 構造体に関数ポインタをいれることで
 インターフェース的な機能を実現 •

    FPROTOはマクロで、プロトタイプ宣言を抑制 • 当時のコンパイラはプロトタイプ宣言があると
 解釈出来ないケースもあり、マクロで抑制
 (プロトタイプ宣言…Cで関数定義時に必要) void (*tp_dealloc) FPROTO((object *));
  25. typeobject typeobject 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*/ };
  26. typeobject typeobject 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*/ };
  27. typeobject typeobject 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Ͱ͸GSFF JOUͰ͸JOU@EFBMMPD͕ UQ@EFBMMPDʹొ࿥ ΦϒδΣΫτ͝ͱʹ ҟͳΔؔ਺Λ ಉ͡ΠϯλʔϑΣʔεͰ ݺͼग़͕͠Մೳ
  28. typeobject typedef 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ͷ ܭࢉͰར༻͢Δ ؔ਺ू߹ͷ
 ߏ଄ମ΁ͷࢀর
  29. 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*/ };
  30. Python3 PyObject • 殆ど構造が同じ typedef struct _object { _PyObject_HEAD_EXTRA Py_ssize_t

    ob_refcnt; struct _typeobject *ob_type; } PyObject; #define PyObject_VAR_HEAD PyVarObject ob_base;
  31. 0.9.1 IntObject • intと言いつつ実際に値が入るob_ivalは
 long型 • typedef struct { OB_HEAD

    long ob_ival; } intobject; extern typeobject Inttype; #define is_intobject(op) ((op)->ob_type == &Inttype)
  32. typedef struct { OB_VARHEAD object **ob_item; } listobject; extern typeobject

    Listtype; #define is_listobject(op) ((op)->ob_type == &Listtype) • リストの構造体
  33. typedef struct { OB_VARHEAD object **ob_item; } listobject; extern typeobject

    Listtype; #define is_listobject(op) ((op)->ob_type == &Listtype) ΦϒδΣΫτͷࢀর഑ྻ ϙΠϯλͷϙΠϯλ • ob_itemはC言語の配列になるので
 O(1)でアクセス可能
  34. >>> 50 + 60 Process 79910 stopped frame #2: 0x00000001000036b9

    python`eval_codeat cev 839 case BINARY_ADD: 840 w = POP(); 841 v = POP(); -> 842 x = add(v, w); 843 DECREF(v); 844 DECREF(w); 845 PUSH(x);
  35. >>> 50 + 60 (lldb) p v (object *) $0

    = 0x00000001002007c0 (lldb) p *v (object) $4 = { ob_refcnt = 2 ob_type = 0x0000000100020848 } (lldb) p *(intobject *)v (intobject) $3 = { ob_refcnt = 2 ob_type = 0x0000000100020848 ob_ival = 50 } object型として扱われている intobjectに キャストすると 値が⾒える!!
  36. Python0.9.1 • Python0.9.1ではテストの掛け算の部分で死ぬ +mumei+anatofuz$ src/python >>> print 1*1 Unhandled exception:

    run-time error: integer overflow Stack backtrace (innermost last): File "<stdin>", line 1
  37. Python0.9.1 • Python0.9.1ではテストの掛け算の部分で死ぬ +mumei+anatofuz$ src/python >>> print 1*1 Unhandled exception:

    run-time error: integer overflow Stack backtrace (innermost last): File "<stdin>", line 1 _⼈⼈⼈⼈⼈⼈⼈⼈⼈⼈⼈⼈_ > 1*1でオーバーフロー!! <  ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^ ̄
  38. Python0.9.1 static object * err_ovf() { err_setstr(OverflowError, "integer overflow"); return

    NULL; } • エラーを文字列として出すだけの関数 • これが呼び出されている部分を探す ➡ intオブジェクトの四則演算で呼びだれている
  39. 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); }
  40. 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
  41. 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
  42. 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
  43. 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
  44. 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
  45. Python0.9.1 x = (double)a * (double)b; if (x > 0x7fffffff

    ||
 x < (double) (long) 0x80000000) return err_ovf(); • 掛け算の結果がintの範囲内に収まっていないと
 err_ovf(オーバーフローを示す例外)の
 関数を返す
  46. Python0.9.1 x = (double)a * (double)b; if (x > 0x7fffffff

    ||
 x < (double) (long) 0x80000000) return err_ovf(); • 0x7fffffffが32bitのintの最大値
 (2^31 -1)
  47. Python0.9.1 x = (double)a * (double)b; if (x > 0x7fffffff

    ||
 x < (double) (long) 0x80000000) return err_ovf(); • 0x7fffffffが32bitのintの最大値
 (2^31 -1) • 0x80000000が32bitのintの最小値
  48. Python0.9.1 x = (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が正解 • バグの原因!!!!!
  49. 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(); }
  50. 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;
  51. 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;
  52. 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; ֻ͚ࢉͳͲʹରԠͨ͠ ൪߸͕ೖ͍ͬͯΔ
  53. 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͸ม਺͚ͩͲʜ
  54. 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; ؔ਺Έ͍ͨʹݺͼग़͍ͯ͠Δ
  55. • そういえば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*/ };
  56. • 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*/
  57. • 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); } }
  58. • 基本的に正の数で表現されている為
 負の数は、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]))
  59. 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; else needspace = 1; } else { printobject(v, fp, 0); needspace = 1; } DECREF(v); break; 13*/5@*5&.
 QSJOUͷΦϖίʔυ
  60. 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; else needspace = 1; } else { printobject(v, fp, 0); needspace = 1; } DECREF(v); break; GXSJUFͰ TUEPVUඪ४ग़ྗʹ ॻ͖ࠐΈ
  61. 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);