有没有简单的方法可以获得最常见的超类#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
答案 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
在这种情况下,A
和B
都是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
inspect.getmro(cls)
代替cls.__mro__
。