【问题标题】:Recursive function involving class涉及类的递归函数
【发布时间】:2018-11-25 11:49:57
【问题描述】:
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]

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

【问题讨论】:

  • 如果你想返回set,为什么不直接使用set
  • 使用'return set(test_list + [x])'
  • 为什么完全使用而不是class.__mro__
  • 不要破坏您的帖子。我已经回滚到以前的健全版本。

标签: python class recursion set


【解决方案1】:

Python 有一个内置的 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 仍然会在多重继承的情况下消除重复项。

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'>}

Python 是 Python,已经有一些东西可以做到这一点:

>>> 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: set 来检查您是否已经访问过课程。它没有消除您添加两次的类,而是一开始只添加一次。在给定许多元素的情况下,setlist 更快。但是,您需要list 来保持顺序。

【讨论】:

    【解决方案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 由于多重继承而出现两次。您可以使用 set 来避免这种情况:

    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
    

    【讨论】:

      猜你喜欢
      • 2017-11-12
      • 2019-02-05
      • 1970-01-01
      • 1970-01-01
      • 2013-06-03
      • 2018-04-02
      • 2017-09-13
      相关资源
      最近更新 更多