2017-01-06 10 views
4

Эта функция предназначена для использования родового словаря (который может рекурсивно содержать другие словари и списки) и помещать все его содержимое в один линейный список.Подробнее pythonic способ записи этой рекурсивной функции

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] 

Он сделал свою работу, но мне интересно: а) я забыл все важные типы данных? (например, я забыл множество) б) что было бы более питоническим способом написать его? (особенно, если я могу написать понимание списка?)

+2

В целом, можно проверить, если элемент итерации или нет (см, если он имеет '__iter__' метода среди его атрибутов), а затем перебрать его элементы. Если вы не думаете, что он слишком велик. С уважением. –

+1

Или используйте абстрактные базовые классы в ['collections.abc'] (https://docs.python.org/3/library/collections.abc.html) –

+4

вы должны использовать' isinstance' вместо 'type (a) == type ({}) '. По крайней мере, 'type (a) == dict'. Но используйте 'isinstance (a, dict)' –

ответ

0

Вы всегда можете избавиться от этих простых типов рекурсии, используя собственный стек/очередь.

Во-первых, используйте надлежащие тесты, предложенные в комментариях, должно быть важно, что вещь __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) 

Там, вероятно, некоторые хитрости, чтобы сделать его более эффективным, например, понимая, что ключи словаря не может быть dicts или списки. Но они могут быть кортежами, и они повторяемы, поэтому ... лучше придерживаться общей проверки.

+0

Может быть, 'things_to_flatten.extend (current)' для 'elif:' clause? В этом случае, возможно, '.extend()' для случая 'dict', также. –

2

Вы можете избежать создания списка/конкатенации в своей функции с помощью 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 Также обратите внимание лучше, чем непосредственно сравнивающих типов.

0

Могу ли я рекомендовать следующее решение? Функции main и make_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() 

 Смежные вопросы

  • Нет связанных вопросов^_^