Pythonic способ конвертировать список диктов в список именованных кортежей - PullRequest
7 голосов
/ 20 октября 2011

У меня есть list из dict.Необходимо преобразовать его в list из namedtuple (предпочтительно) или в простое tuple, в то время как первая переменная разделяется на пробелы.

Какой еще питонический способ сделать это?1010 * Я немного упростил свой код.Приветствуются понимания, выражения gen и использование itertools.

Данные:

dl = [{'a': '1 2 3',
       'd': '*',
       'n': 'first'},
      {'a': '4 5',
       'd': '*', 'n':
       'second'},
      {'a': '6',
       'd': '*',
       'n': 'third'},
      {'a': '7 8 9 10',
       'd': '*',
       'n': 'forth'}]

Простой алгоритм:

from collections import namedtuple

some = namedtuple('some', ['a', 'd', 'n'])

items = []
for m in dl:
    a, d, n = m.values()
    a = a.split()
    items.append(some(a, d, n))

Вывод:

[some(a=['1', '2', '3'], d='*', n='first'),
 some(a=['4', '5'], d='*', n='second'),
 some(a=['6'], d='*', n='third'),
 some(a=['7', '8', '9', '10'], d='*', n='forth')]

Ответы [ 4 ]

7 голосов
/ 20 октября 2011

Ниже @Petr Viktorin указывает на проблему с моим первоначальным ответом и вашим первоначальным решением:

ВНИМАНИЕ!Значения () словаря расположены не в определенном порядке!Если это решение работает, и a, d, n действительно возвращаются в таком порядке, это просто совпадение.Если вы используете другую версию Python или создаете дикты по-другому, она может сломаться.

(Я немного огорчен, что не поднял это с самого начала, иполучил 45 повторений за это!)

Вместо этого используйте предложение @ eryksun:

items =  [some(m['a'].split(), m['d'], m['n']) for m in dl]

Мой оригинальный, неправильный ответ.Не используйте его, если у вас нет списка OrderedDict.

items =  [some(a.split(), d, n) for a,d,n in (m.values() for m in dl)]
3 голосов
/ 07 января 2014

Думаю, что я буду звонить сюда только потому, что я так сильно люблю именованные кортежи и словари!

Вот понимание списка со словосочетанием в нем, которое может сделать вашу первоначальную обработку словаря:

split_dictionaries = \ 
    [{key: value.split() for k, value in d.iteritems()} for d in dl] 

Я часто использую рецепт, который я называю «tupperware», который рекурсивно преобразует словари в именованные кортежи.См суть здесь , для кода.Вот упрощенная часть, чтобы интегрировать ее здесь, и у вас есть довольно чистый способ выполнения этой операции.

import collections

def namedtuple_from_mapping(mapping, name="Tupperware"):
    this_namedtuple_maker = collections.namedtuple(name, mapping.iterkeys())
    return this_namedtuple_maker(**mapping)

Итак, с помощью этой функции вы можете сделать это - что мы вскоре изобразим рефакторингом:

split_namedtuples = [ 
    namedtuple_from_mapping(
        {key: value.split() for k, value in d.iteritems()}
    ) for d in dl
]   

А теперь с лучшей инкапсуляцией и удобочитаемостью:

def format_string(string):
    return string.split()

def format_dict(d):
    return {key: format_string(value) for key, value in d.iteritems()}

formatted_namedtuples = [namedtuple_from_mapping(format_dict(d)) for d in dl]
2 голосов
/ 20 октября 2011

В дополнение к ответу, предоставленному @detly, если вы не знаете о полях диктовок ранее, вы можете создать класс namedtuple с

some = namedtuple('some', set(k for k in d.keys() for d in dl))
2 голосов
/ 20 октября 2011

Другой вариант, не уверен, что он лучше или хуже других:

class some(namedtuple('some', 'a d n')):
    def __new__(cls, **args):
        args['a'] = args['a'].split()
        return super(some, cls).__new__(cls, **args)

items = list(some(**m) for m in dl)

Кстати, я не совсем обязую дать базовому классу то же имя, что и подклассу some. Мне это нравится, потому что это означает, что результирующий класс конвертируется в строку, используя имя some, и это никогда не вызывало у меня особых проблем, но потенциально это может сбить с толку, если вы отлаживаете имена классов. Так что делай это осторожно.

Или та же идея, используя разные трюки:

some = namedtuple('some', 'a d n')

def make_some(args):
    args = args.copy()
    args['a'] = args['a'].split()
    return some(**args)

items = map(make_some, dl) # NB: this doesn't return a list in Python 3
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...