2016-10-20 6 views
3

ドキュメントがあると言う:__subclasses__メソッドはCPythonでどのように実装されていますか?

Each class keeps a list of weak references to its immediate subclasses. This method returns a list of all those references still alive.

しかし、どのように、各クラスは、最初の場所でそのサブクラスへの弱参照のリストを取得していますか?言い換えれば、私は

class B(A): 
    pass 

を作成するときにどのようにABはそれをサブクラス化することを見つけるのですか?このメカニズムは、エッジケース(カスタムメタクラス、__bases__への割り当てなど)に耐えられるほど堅牢ですか?

+0

これに注意して、私はすべてのクラス宣言がインポートされるまであなたが '__subclasses__'を使わないようにしなければならないという困難な方法を学びました。 – wim

+0

@wimすべての輸入がすでに行われていることを確認するのはやや難しいかもしれませんが、このように動作することは非常に合理的です。結局のところ、これは実行時のメカニズムです(必然的に、クラスの作成はPythonでは動的なので)。だから、与えられたpythonプロセスで現在生きているクラスについてのみ教えてくれるはずです。 – max

+0

これは当てはまります。インポートの順序を適切に制御しない限り、インポート時には使用できないということです。それは聞こえるよりも難しく、リファクタリング時には壊れやすくなります。 – wim

答えて

5

新しいクラスの初期化の一部として、そのクラスへの弱い参照が、それぞれの基本クラスのメンバーに追加されます(tp_subclasses)。あなたはObjects/typeobject.cでPythonのソースコードでこれを見ることができます:

int 
PyType_Ready(PyTypeObject *type) 
{ 
    ... 
    /* Link into each base class's list of subclasses */ 
    bases = type->tp_bases; 
    n = PyTuple_GET_SIZE(bases); 
    for (i = 0; i < n; i++) { 
     PyObject *b = PyTuple_GET_ITEM(bases, i); 
     if (PyType_Check(b) && 
      add_subclass((PyTypeObject *)b, type) < 0) 
      goto error; 
    } 
    ... 
} 

static int 
add_subclass(PyTypeObject *base, PyTypeObject *type) 
{ 
    int result = -1; 
    PyObject *dict, *key, *newobj; 

    dict = base->tp_subclasses; 
    if (dict == NULL) { 
     base->tp_subclasses = dict = PyDict_New(); 
     if (dict == NULL) 
      return -1; 
    } 
    assert(PyDict_CheckExact(dict)); 
    key = PyLong_FromVoidPtr((void *) type); 
    if (key == NULL) 
     return -1; 
    newobj = PyWeakref_NewRef((PyObject *)type, NULL); 
    if (newobj != NULL) { 
     result = PyDict_SetItem(dict, key, newobj); 
     Py_DECREF(newobj); 
    } 
    Py_DECREF(key); 
    return result; 
} 

__bases__ためsetterも古いものと新しい拠点のそれぞれのサブクラスのリストを更新します。

static int 
type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context) 
{ 
    ... 
    if (type->tp_bases == new_bases) { 
     /* any base that was in __bases__ but now isn't, we 
      need to remove |type| from its tp_subclasses. 
      conversely, any class now in __bases__ that wasn't 
      needs to have |type| added to its subclasses. */ 

     /* for now, sod that: just remove from all old_bases, 
      add to all new_bases */ 
     remove_all_subclasses(type, old_bases); 
     res = add_all_subclasses(type, new_bases); 
     update_all_slots(type); 
    } 
    ... 
} 

注意をメタクラスがない場合ということサブクラス関係の意味をカスタマイズするためのもの、__subclasses__はそれを反映しません。たとえば、issubclass(list, collections.abc.Iterable)Trueですが、から始まる__subclasses__ツリーの検索ではlistは表示されません。

+0

これは、 'cls .__ bases__'への代入が' __subclasses__'を通してサブクラスを見つける能力を壊すことを意味しますか? – max

+0

'TypeError:__bases__代入: 'D'デアロケータは' __bases__ 'に代入しようとすると' object''と異なります。 –

+1

@max:いいえ、 '__bases__'の設定者はリストを最新の状態に保ちます。 – user2357112

関連する問題