PyObject の基礎

話すこと

  • C で OOP を作るには?
    • そもそも OOP って?
    • C ではどうするの?
    • Python ではどうしてるの?

お前, 誰よ?

  • @cocoatomo

  • B2B の基幹業務パッケージを作る会社
    • 研究開発部門みたいなとこ
    • 普段は Java 使い
  • Python との出会い: 仕事で Jython

  • Python document の翻訳やってます

OOP って?

ここでは「データと処理を近いところに集めたもの」くらいの定義

.oO (宗教論争は懇親会で…… :P

Wikipedia (en) では

こんな感じらしいっす.

  • Dynamic dispatch (switching implementation)
  • Encapsulation
  • Subtype polymorphism
  • Object inheritance (or delegation)
  • Open recursion (“this” or “self”)

追加の特徴らしいっす

  • Classes of objects
  • Instances of classes
  • Methods
  • Message passing
  • Abstraction

c.f. http://en.wikipedia.org/wiki/Object-oriented_programming#Fundamental_concepts_and_features

C での OOP は...

  • クラス (型): 構造体
  • インスタンス: 構造体オブジェクト
  • フィールド: 構造体のメンバー
  • メソッド: 構造体のメンバー (関数ポインタ)

→ 何はともあれ例をば...

クラス定義

Python で書いたこんなクラス

class Rect(object):
    def __init__(self):
        self.height = 0
        self.width = 0

    def rotate(self):
        self.height, self.width = \
            self.width, self.height

クラス定義

C だとまず構造体を書く

struct rect {
    int height;
    int width;
    void (*rotate)(struct rect *);
};

→ 次はメソッド定義

メソッド定義

void rect_init(struct rect *self)
{
    self->height = 0;
    self->width = 0;
    self->rotate = *rect_rotate;
}

void rect_rotate(struct rect *self)
{
    int tmp = self->height;
    self->height = self->width;
    self->width = tmp;
}

クラスの使用 (Python)

クラスを使う側のコードはこんな感じ

rect = Rect()
rect.height = 3
rect.width = 4

print('height: {0.height}, width: {0.width}'.format(rect))
rect.rotate()
print('height: {0.height}, width: {0.width}'.format(rect))

クラスの使用 (C)

C だとこんな感じ

int main(void)
{
    struct rect r;
    rect_init(&r);
    r.height = 3;
    r.width = 4;

    printf("height: %d, width: %d\n", r.height, r.width);
    r.rotate(&r);
    printf("height: %d, width: %d\n", r.height, r.width);
    return 0;
}

C での OOP の特徴

  • name mangling: rect_*
  • 関数の第 1 引数は self
  • これはまさに ADT (Abstract Data Type)

完全なソースコードはここにあります.

https://gist.github.com/972194

OOP になっているの?

さっき挙げた10個の特徴は C で実現できてるの?

→ 1つ1つ順に見ていく

OOP になっているの? その1

  • Dynamic dispatch

    → C では無理

  • Encapsulation

    → ヘッダファイル と static function で file scope を実現

OOP になっているの? その2

  • Subtype polymorphism

    → dynamic dispatch が無いので無理

  • Object inheritance

    → 構造体で Decorator パターン (後で詳しく)

OOP になっているの? その3

  • Open recursion

    → 関数の第 1 引数を this や self にして自分自身を入れるルールにする. あれ? どこかで……

  • Classes of objects

    → 構造体

OOP になっているの? その4

  • Instances of classes

    → 構造体オブジェクト

  • Methods

    → 構造体メンバー (関数ポインタ)

OOP になっているの? その5

  • Message passing

    → 無し

  • Abstraction

    → 継承で実現

ということで継承を詳しく見よう. 継承は入れ子になった構造体を上手く利用している.

構造体の構造

こんな構造体があったら

struct int_long {
    int a;
    long b
};

メモリ配置

メモリ配置はだいたいこんな感じ

 <--- struct int_long --->
+--------+----------------+
|<- a -->|<----- b ------>|
+--------+----------------+
 <-int -> <--- long ----->

キャストで継承

C にはキャストという仕組みがある.

構造体で使うと, コンパイラに渡すメモリ配置の情報を変更する.

COB... と似ているわけだが……

キャストの例

struct base {
        int basic_field;
};

struct extended {
        struct base base;
        int extended_field;
};

int main(void)
{
    struct base *base_obj;
    struct extended *obj = malloc(sizeof(struct extended));

    // 省略...

    base_obj = (struct base *) obj;
}

何が起きているか?

 <---------- struct extended -------->
 <- struct base ->
+-----------------+--------------------+
|<- basic_field ->|<- extended_field ->|
+-----------------+--------------------+
 ↓
CAST (struct base *)
 ↓
 <- struct base ->
+-----------------+
|<- basic_field ->|
+-----------------+

ソースコード

完全なソースコードはここにあります. https://gist.github.com/972214

PyObject

基底クラスを見ていこう.

/* Include/object.h */

/* 実際には PyObject の中には何も無いが,
 * 全ての Python オブジェクトへのポインタは
 * PyObject* にキャストできる.
 * これは手作業による継承だ.
 * 同様に不定サイズを持つ
 * Python オブジェクトへのポインタは
 * PyVarObject* にキャストできる.
 */
typedef struct _object {

PyObject

定義はこんな感じ.

typedef struct _object {
    _PyObject_HEAD_EXTRA
    Py_ssize_t ob_refcnt; // 参照カウンタ用
    struct _typeobject *ob_type; // メソッドの塊
} PyObject;

PyTypeObject

メソッド部分はどうなってるのか?

/* Include/object.h */
typedef void (*destructor)(PyObject *);
typedef int (*printfunc)(PyObject *, FILE *, int);

// メソッドの塊
typedef struct _typeobject {
   /* Methods to implement standard operations */

   destructor tp_dealloc;
   printfunc tp_print;
   // 以下続く...
} PyTypeObject;

具体例をば

dict の実装を見て行く

/* Include/object.h */
/* PyObject_HEAD defines the initial segment of every PyObject. */
#define PyObject_HEAD PyObject ob_base;

/* Include/dictobject.h */
// dict 型の定義
typedef struct _dictobject PyDictObject;
struct _dictobject {
    PyObject_HEAD // この中に PyTypeObject がある
    // dict 独自のメンバー ...
};

dict のメソッド

/* dictobject.c */
PyTypeObject PyDict_Type = {
    // dict 用のメソッドがたくさん...
    mapp_methods, /* tp_methods */
    // ...
};

PyObject *
PyDict_New(void)
{
    // PyDict_Type を使ってオブジェクトを作っている...
    mp = PyObject_GC_New(PyDictObject, &PyDict_Type);
    // ...
}

PyObject_GC_New

/* Include/objimpl.h */
#define PyObject_GC_New(type, typeobj) \
                ( (type *) _PyObject_GC_New(typeobj) )

/* Module/gcmodule.c */
// ようやくそれっぽいところに
PyObject *
_PyObject_GC_New(PyTypeObject *tp)
{
    PyObject *op = _PyObject_GC_Malloc(_PyObject_SIZE(tp));
    if (op != NULL) // 型情報を使って初期化
        op = PyObject_INIT(op, tp);
    return op;
}

PyObject_Init

/* Objects/object.c */
PyObject *
PyObject_Init(PyObject *op, PyTypeObject *tp)
{
    if (op == NULL)
        return PyErr_NoMemory();
    /* Any changes should be reflected in PyObject_INIT (objimpl.h) */
    Py_TYPE(op) = tp; // 型情報を代入している!
    _Py_NewReference(op);
    return op;
}

/* Include/object.h */
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)

呼び出すときは?

/* Objects/abstract.c */
// __getitem__ の実装
PyObject *
PyObject_GetItem(PyObject *o, PyObject *key)
{
    PyMappingMethods *m;
    // ...

    m = o->ob_type->tp_as_mapping;
    if (m && m->mp_subscript)
        return m->mp_subscript(o, key);
    // ...
}

まとめると

メソッドの塊 PyDict_Type を定義して, それを PyObject に入れて dict オブジェクトを作っている.

全体のまとめ

  • C でも頑張れば OOP できる!! (実態は ADT の延長だけど)
  • Python は OOP を C で綺麗に実現している.