【问题标题】:More pythonic way of writing this recursive function编写此递归函数的更多 Pythonic 方式
【发布时间】:2017-01-06 20:50:07
【问题描述】:

此函数旨在获取一个通用字典(可能递归地包含其他字典和列表)并将其所有内容放在一个线性列表中。

def make_a_list(a):
  print type(a)
  if (type(a) == type({})):
    return make_a_list(a.keys()) + make_a_list(a.values())
  elif (type(a) == type([])):
    if len(a) > 1:
      return make_a_list(a[0]) + make_a_list(a[1:])
    return a
  else:
    return [a]

它完成了它的工作,但我想知道: a) 我是否忘记了任何重要的数据类型? (例如,我忘记了套装) b) 什么是更 Pythonic 的方式来编写它? (特别是我可以写一个列表理解吗?)

【问题讨论】:

  • 更一般地,你可以检查一个元素是否是可迭代的(看看它的属性中是否有__iter__方法)然后迭代它的元素。除非你认为这种方式太大。问候。
  • 或者使用collections.abc中的抽象基类
  • 您应该使用isinstance 而不是type(a) == type({})。至少,type(a) == dict。但是使用isinstance(a, dict)
  • 另外,make_a_list(a.keys()) 可能只是make_a_list(a)。但是,这可能不是必需的,因为键不能是 dicts 或 lists,因为两者都是不可散列的。
  • 如果这是您认为可以改进的工作代码,请参阅Code Review。如果没有,请查看How to Ask 并澄清问题。

标签: python recursion list-comprehension


【解决方案1】:

您可以使用yield 避免在函数中创建列表/连接。

def make_a_list(a):
  if isinstance(a, dict):
    yield from make_a_list(a.keys())
    yield from make_a_list(a.values())
  elif isinstance(a, (list, tuple, set)):
    for x in a:
      yield from make_a_list(x)
  else:
    yield a

这是一个生成器,所以如果你真的需要一个列表,你可以这样做:

def make_a_real_list(a):
    return list(make_a_list(a))

还要注意isinstance 比直接比较类型要好。

【讨论】:

    【解决方案2】:

    我可以推荐以下解决方案吗? mainmake_a_list 函数测试你的想法并分别展示一个更好的方法来实现它。如果您不介意使用可迭代对象和生成器的概念,test 函数和flatten 生成器可能是解决问题的更好示范。您可以根据最需要的内容调整代码并提供最佳性能。

    #! /usr/bin/env python3
    def main():
        obj = 1
        print('make_a_list({!r}) = {!r}'.format(obj, make_a_list(obj)))
        obj = {1, 2, 3}
        print('make_a_list({!r}) = {!r}'.format(obj, make_a_list(obj)))
        obj = [1, 2, 3]
        print('make_a_list({!r}) = {!r}'.format(obj, make_a_list(obj)))
        obj = [1]
        print('make_a_list({!r}) = {!r}'.format(obj, make_a_list(obj)))
        obj = 'a', 'b', 'c'
        print('make_a_list({!r}) = {!r}'.format(obj, make_a_list(obj)))
        obj = {1: 2, 3: 4, 5: 6}
        print('make_a_list({!r}) = {!r}'.format(obj, make_a_list(obj)), end='\n\n')
    
    
    def make_a_list(obj):
        if isinstance(obj, dict):
            return make_a_list(list(obj.keys())) + make_a_list(list(obj.values()))
        if isinstance(obj, list):
            if len(obj) > 1:
                return make_a_list(obj[0]) + make_a_list(obj[1:])
            return obj
        return [obj]
    
    
    def test():
        obj = 1
        print('list(flatten({!r})) = {!r}'.format(obj, list(flatten(obj))))
        obj = {1, 2, 3}
        print('list(flatten({!r})) = {!r}'.format(obj, list(flatten(obj))))
        obj = [1, 2, 3]
        print('list(flatten({!r})) = {!r}'.format(obj, list(flatten(obj))))
        obj = [1]
        print('list(flatten({!r})) = {!r}'.format(obj, list(flatten(obj))))
        obj = 'a', 'b', 'c'
        print('list(flatten({!r})) = {!r}'.format(obj, list(flatten(obj))))
        obj = {1: 2, 3: 4, 5: 6}
        print('list(flatten({!r})) = {!r}'.format(obj, list(flatten(obj))))
    
    
    def flatten(iterable):
        if isinstance(iterable, (list, tuple, set, frozenset)):
            for item in iterable:
                yield from flatten(item)
        elif isinstance(iterable, dict):
            for item in iterable.keys():
                yield from flatten(item)
            for item in iterable.values():
                yield from flatten(item)
        else:
            yield iterable
    
    
    if __name__ == '__main__':
        main()
        test()
    

    【讨论】:

      【解决方案3】:

      您始终可以使用自己的堆栈/队列摆脱这些简单类型的递归。

      首先,使用 cmets 中建议的正确测试,重要的是 __iter__ 东西。

      然后:

      things_to_flatten = []
      things_to_flatten.append(a)
      new_list = []
      while things_to_flatten:
        current = things_to_flatten.pop(0)
        if isinstance(current, dict):
            things_to_flatten.extend(current.keys())
            things_to_flatten.extend(current.values())
        elif hasattr(current, '__iter__'):
            things_to_flatten.extend(current)
        else:
            new_list.append(current)
      

      可能需要进行一些调整以提高效率,例如意识到字典键不能是字典或列表。但是,它们可能是元组,并且它们是可迭代的,所以...最好坚持进行一般检查。

      【讨论】:

      • 也许things_to_flatten.extend(current) 用于elif: 子句?就此而言,.extend() 也可能用于 dict 案例。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-15
      • 2011-03-09
      相关资源
      最近更新 更多