最伟大的共同超类

时间:2014-09-11 11:43:29

标签: python superclass

有没有简单的方法可以获得最常见的超类#34;对象列表?例如,如果

class A(object): pass
class B(A): pass
class C(A): pass
class D(B, C): pass
class E(C): pass
class F(D, E): pass

b = B()
d = D()
e = E()

然后

gcs(b, e) is A
gcs(d, e) is C
gcs(e, e) is E

3 个答案:

答案 0 :(得分:4)

基于Martijn的想法,但考虑到时间复杂性不会成为一个问题,请使用更简单的方法(感谢@veedrac的投入):

def gcs(*instances):
    classes = [type(x).mro() for x in instances]
    for x in classes[0]:
        if all(x in mro for mro in classes):
            return x

print gcs(b, e)
print gcs(d, e)
print gcs(e, e)

<强>输出:

<class '__main__.A'>
<class '__main__.C'>
<class '__main__.E'>

使用集合的上述代码的略微变体:

def gcs(*instances):
    mros = (type(ins).mro() for ins in instances)
    mro = next(mros)
    common = set(mro).intersection(*mros)
    return next((x for x in mro if x in common), None)

答案 1 :(得分:3)

这基本上是一个简化的最长共同后续问题;比较MRO序列并返回其索引的最低总和类型:

def gcs(a, b):
    """Find the common base class between two classes or instances"""
    try:
        a, b = a.mro(), b.mro()
    except AttributeError:
        a, b = type(a).mro(), type(b).mro()
    a_idx, b_idx = {t: i for i, t in enumerate(a)}, {t: i for i, t in enumerate(b)}
    try:
        return min(a_idx.viewkeys() & b_idx.viewkeys(),
                   key=lambda t: a_idx[t] + b_idx[t])
    except ValueError:
        return None

这是O(M + N)算法,其中M和N是两个对象的MRO的大小。该函数可以处理类和实例。

使用示例对象进行演示:

>>> gcs(e, b)
<class '__main__.A'>
>>> gcs(e, d)
<class '__main__.C'>
>>> gcs(e, e)
<class '__main__.E'>

答案 2 :(得分:1)

我认为这里的一个问题是你可以拥有多个最常见的超类 - 采用类继承结构,如

AB ---> A
   \ /
    x
   / \
BA ---> B

在这种情况下,AB都是gca([AB, BA])的合法答案。

在我自己的代码中遇到这个后,我意识到我需要将问题重新构造成:

  

查找指定类列表共享的最小公共基础集;定义为所有类,1)是原始列表中所有内容的父类,2)没有子类也包含在返回列表中。

     

这最后一个要求只负责获取“最近”的类,但处理有多个这样的值的情况。

以下代码实现了这一点:

def minimal_common_bases(classes):
    # pull first class, and start with it's bases
    gen = iter(classes)
    cls = next(gen, None)
    if cls is None:
        return set()
    common = set(cls.__mro__)

    # find set of ALL ancestor classes,
    # by intersecting MROs of all specified classes
    for cls in gen:
        common.intersection_update(cls.__mro__)

    # remove any bases which have at least one subclass also in the set,
    # as they aren't part of "minimal" set of common ancestors.
    result = common.copy()
    for cls in common:
        if cls in result:
            result.difference_update(cls.__mro__[1:])

    # return set
    return result
  • 注意:在py2下,您需要使用inspect.getmro(cls)代替cls.__mro__