涉及类的递归函数

时间:2018-11-25 11:49:57

标签: python class recursion set

def check_classes(cls):
    if len(cls.__bases__) == 0:
        return []
    else:
        test_list = []
        for x in range(len(cls.__bases__)):
            test_list += [cls] + check_classes(cls.__bases__[x])
        for x in cls.__bases__:
            return test_list + [x]

我目前有一个递归函数,该函数将一个类作为其参数并返回所有基类的列表。这可以正常工作,但是列表中有许多重复的类。我想返回一个集合而不是一个列表,并且想知道如何更改代码来做到这一点。

2 个答案:

答案 0 :(得分:3)

Python具有内置的background-image: url(../images/background/large/static.png), url(../images/background/large/repeat.png); background-repeat: no-repeat, repeat-y; background-position: 0px top, 600px top; 类型,可消除重复项:

set

此代码已经避免了首先添加许多重复项。但是,def get_bases(obj): bases = {obj} # new set including only obj if not(obj.__bases__): # technically redundant - iter is a noop on empty collections return bases else: for x in obj.__bases__: bases.update(get_bases(x)) # update set - automatically eliminates duplicates return bases 仍然可以消除多重继承情况下的重复项。

set

Python是Python,已经有一些方法可以做到这一点:

class A: ...
class B1(A): ...
class B2(A): ...
class C(B1, B2): ...
print(get_bases(C))
# {<class '__main__.C'>, <class '__main__.B1'>, <class 'object'>, <class '__main__.B2'>, <class '__main__.A'>}

如果您只关心基础,请使用>>> C.__mro__ (__main__.C, __main__.B1, __main__.B2, __main__.A, object) 。它的顺序还表示如何在多个碱基之间进行查找。


这种搜索的一种略有不同的方法是使用__mro__来跟踪重复项,而使用set来存储 元素:

list

这使用def get_bases(obj, _dupes=None): _dupes = _dupes if _dupes is not None else set() bases = [obj] # new list including only obj _dupes.add(obj) for x in obj.__bases__: if x not in _dupes: bases.extend(get_bases(x, _dupes)) # update set - automatically eliminates duplicates return bases 检查您是否已经上过课。无需消除添加两次的类,而是仅将它们添加一次。给定许多元素,此检查_dupes: setset更快。但是,您需要list才能保留顺序。

答案 1 :(得分:2)

您的功能是多余的,可以用cls.__mro__代替:

>>> class Base: pass
...
>>> class Foo(Base): pass
...
>>> class Bar(Base): pass
...
>>> class Baz(Foo, Bar): pass
...

>>> Baz.__mro__
(<class '__main__.Baz'>, <class '__main__.Foo'>, <class '__main__.Bar'>, <class '__main__.Base'>, <class 'object'>)

最大的问题是您的实现将类添加到列表两次中,一次是在递归调用中,一次是在当前调用中。仅将 current 类添加到列表中。将__bases__列表检查为空也是多余的,因为如果序列为空,则for循环已经不执行任何操作。

这足够了:

def check_classes(cls):
    result = [cls]
    for base in cls.__bases__:
        result += check_classes(base)
    return result

但这仍然会重复多次包含在层次结构中的基类:

>>> check_classes(Baz)
[<class '__main__.Baz'>, <class '__main__.Foo'>, <class '__main__.Base'>, <class 'object'>, <class '__main__.Bar'>, <class '__main__.Base'>, <class 'object'>]

请注意,由于多重继承,Baseobject出现了两次。您可以使用一组来避免这种情况:

def check_classes(cls):
    result = set([cls])
    for base in cls.__bases__:
        result.update(check_classes(base))
    return result

这时我们会失去订购,但这可能足以满足您的需求:

>>> check_classes(Baz)
{<class '__main__.Bar'>, <class '__main__.Foo'>, <class '__main__.Base'>, <class 'object'>, <class '__main__.Baz'>}

但是,您只需使用set(cls.__mro__)并完成操作即可:

>>> check_classes(Baz) == set(Baz.__mro__)
True