Python:__ subclasses__顺序

时间:2018-07-08 14:43:12

标签: python subclass

我有一些代码可以通过在基类上调用__subclasses__()函数来创建子类列表(实例)。

subclasses = [subclass() for subsclass in BaseClass.__subclasses__()]

在Python 2.7中,此列表中子类的顺序始终等于我代码中import语句的顺序(每个子类在其自己的Python文件中定义)。但是在Python 3.5中,此列表中子类的顺序似乎是随机的。我可以使用某种解决方法在Python 3.5中获得相同的行为吗?

请参见class.__subclasses__ documentation

1 个答案:

答案 0 :(得分:7)

在Python 3.5中,没有,或者没有任何琐碎的手段。 As of Python 3.4 tp_subclasses数据结构通过字典跟踪子类(将子类的id()值映射为弱引用,原因参见Python issue #17936)。字典是无序的。

在Python 3.6中,字典的内部实现已更改,现在可以跟踪插入顺序。升级到Python 3.6时,您会再次按照插入顺序(子级首先被Python看到的顺序)获得子类。

但是,在字典中跟踪子类的事实是一个实现细节,因此不应真正依赖它。

您可以通过其他方式跟踪订单。您可以给基类一个元类,该元类按顺序记录子类,例如:

from itertools import count
from weakref import WeakKeyDictionary

class SubclassesInOrderMeta(type):
    _subclass_order = WeakKeyDictionary()
    _counter = count()

    def __ordered_subclasses__(cls):
        # sort subclasses by their 'seen' order; the sort key
        # puts classes that are missing from the _subclass_order
        # mapping at the end, defensively.
        order_get = type(cls)._subclass_order.get
        sort_key = lambda c: order_get(c, float('inf'))
        return sorted(cls.__subclasses__(), key=sort_key)

    def __new__(mcls, *args, **kwargs):
        cls = super(SubclassesInOrderMeta, mcls).__new__(mcls, *args, **kwargs)
        mcls._subclass_order[cls] = next(mcls._counter)
        return cls