【问题标题】:How is __subclasses__ method implemented in CPython?__subclasses__ 方法在 CPython 中是如何实现的?
【发布时间】:2016-10-20 21:31:25
【问题描述】:

文档说:

每个类都有一个对其直接子类的弱引用列表。此方法返回所有仍然存在的引用的列表。

但是每个类首先如何获得对其子类的弱引用列表呢?换句话说,当我创建

class B(A):
  pass

A 如何发现 B 只是对其进行子类化?这种机制是否足够强大,可以在边缘情况下生存(自定义元类、分配给__bases__ 等)?

【问题讨论】:

  • 小心这个,我学到了一个艰难的方法,你必须确保在导入所有类声明之前不要使用__subclasses__,否则可能会丢失一些项目。
  • @wim 虽然确保所有导入都已经发生可能有点棘手,但这样工作似乎非常合理。毕竟,这是一种运行时机制(必然,因为类的创建在 python 中是动态的)。所以它只能告诉我们当前在给定 python 进程中存在的类。
  • 确实如此——推论是你不能在导入时使用它,除非你对导入顺序有很好的控制。这比听起来更难,并且在重构时容易改变。

标签: python python-3.x python-internals


【解决方案1】:

作为新类初始化的一部分,对该类的弱引用被添加到其每个基类的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,但list 不会出现在从collections.abc.Iterable 开始的__subclasses__ 树的搜索中。

【讨论】:

  • 所以这意味着分配给cls.__bases__ 将破坏通过__subclasses__ 查找子类的能力?
  • 我在尝试分配给__bases__时得到TypeError: __bases__ assignment: 'D' deallocator differs from 'object'
  • @max:不,__bases__ 的设置器将保持列表是最新的。
  • @AnthonySottile 在 python 3 中,只能分配给__bases__ when the class doesn't directly subclass object
猜你喜欢
  • 2012-12-17
  • 2020-08-14
  • 2012-01-13
  • 2013-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-21
  • 2011-10-02
相关资源
最近更新 更多