如何以Pythonic方式将此元组列表转换为此dict?

时间:2014-01-21 17:42:39

标签: python dictionary

我有一个元组列表

tuplist = [('person', u'English : 1, 2, 3 ; Dutch : 5, 6, 7'), ('home', u'English : 8, 9, 10; Dutch: 11, 12, 13')]

我想将此转换为此特定字典

{'person': {u'Dutch': [u'5', u'6', u'7'], u'English': [u'1', u'2', u'3']}, 'home': {u'Dutch': [u'11', u'12', u'13'], u'English': [u'8', u'9', u'10']}}

目前我这个:

dic = dict(tuplist)
final_dic = {}
for x in dic:
    str = dic[x]
    list1 = [y.strip() for y in str.split(';')]
    subdict = {}
    for z in list1:
        list2 = [y.strip() for y in z.split(':')]
        subdict[list2[0]] = [y.strip() for y in list2[1].split(',')]
    final_dic[x] = subdict

但是我想把它重写为Pythonic。有人有想法吗?

3 个答案:

答案 0 :(得分:5)

您可以嵌套一组字典和列表推导:

{k: {l.strip(): [n.strip() for n in nums.split(',')] 
     for i in v.split(';') 
     for l, nums in (i.split(':', 1),)}
 for k, v in tuplist}

这是相当满口的,所以最好将语言字典拆分为生成器:

def language_values(line):
    for entry in line.split(';'):
        lang, nums = entry.split(':', 1)
        yield lang.strip(), [n.strip() for n in nums.split(',')]

{k: dict(language_values(v)) for k, v in tuplist}

任何一个产生所需的输出:

>>> {k: {l.strip(): [n.strip() for n in nums.split(',')] 
...      for i in v.split(';') 
...      for l, nums in (i.split(':', 1),)}
...  for k, v in tuplist}
{'person': {u'Dutch': [u'5', u'6', u'7'], u'English': [u'1', u'2', u'3']}, 'home': {u'Dutch': [u'11', u'12', u'13'], u'English': [u'8', u'9', u'10']}}
>>> def language_values(line):
...     for entry in line.split(';'):
...         lang, nums = entry.split(':', 1)
...         yield lang.strip(), [n.strip() for n in nums.split(',')]
... 
>>> {k: dict(language_values(v)) for k, v in tuplist}
{'person': {u'Dutch': [u'5', u'6', u'7'], u'English': [u'1', u'2', u'3']}, 'home': {u'Dutch': [u'11', u'12', u'13'], u'English': [u'8', u'9', u'10']}}

答案 1 :(得分:1)

对不起,@ colicab,我想你知道我提到的很多这些事情。只是我开始给你写一个答案,最后得到了一个受你的挑战启发的更一般的答案。这可能是比大多数人更多的个人答案,但我想大多数人都会认为是pythonic。

什么是 pythonic 总是有争议的。让我们看看Python中的一些工具,通常被认为是pythonic(即在Python脚本中优雅且有用),可以帮助我们。

函数(documentation

我说最蟒蛇的事情是慷慨地创造功能。例如,您将使用逗号分隔的数字字符串,并且需要将其转换为int的列表。我们应该创建一个函数来生成列表:

def parse_numbers(numbers_string):
    pass

# Little assert to ensure it works
assert parse_numbers('1, 2, 3') == [1, 2, 3]

好的,我们的函数暂时没有任何功能,但我们可以使用...

来解决它

str.split()documentation

str.strip()documentation

int()构造函数(documentation

我们可以使用str.split()方法轻松获取逗号之间的字符串列表:

>>> "1, 2, 3".split(',')
['1', ' 2', ' 3']

然而,这(单独)并不能解决我们的问题。首先,因为列表中的字符串有空格。可以使用str.strip()方法解决它:

>>> '  1   '.strip()
'1'

我们更接近int的列表,当然,但还没有。毕竟,'1'是包含数字的字符串,而不是整数(和in Python those are very different things)。当然,我们可以使用int()构造函数轻松解决它:

>>> int('1')
1
>>> int('  1   '.strip())
1

现在,我们如何将此操作应用于下面列表中的所有字符串?

列表推导(documentation

很多时候,您需要从其他列表创建列表。在其他地方,我们用来创建一个空列表并用东西填充它。然而, pythonic 方式涉及列表推导。例如,下面的行将获取拆分产生的每个元素,从中删除所有空格,并将剥离的结果转换为int。毕竟,这些操作的结果将被放入一个新列表中:

>>> [int(n.strip()) for n in '1, 2  , 3   '.split(',')]
[1, 2, 3]

numbers_string参数应用相同的逻辑,我们可以得到下面的漂亮函数:

def parse_numbers(numbers_string):
    return [int(n.strip()) for n in numbers_string.split(',')]

# Little assert to ensure it works    
assert parse_numbers('1, 2, 3') == [1, 2, 3]

简单,有凝聚力,清晰 - 确实,pythonic。

现在,我们做什么?我们应该得到语言名称和数字列表。要做到这一点,我们回到第一个答案:一个功能!但为了使它成功,我们将使用非常pythonic ......

序列打包和解包(documentation

我们的下一个功能将使用'English : 1, 2'等字符串。并返回适合在dict construtor中使用的一对:

def parse_language(language_string):
    language, numbers_string = language_string.split(':')
    return language.strip(), parse_numbers(numbers_string)

# Little assert to ensure it works 
assert parse_language('English : 1, 2, 3') == ('English', [1, 2, 3])

我们已经知道strip()split()。新的魔力是split(':')调用将返回一个包含两个值的列表 - 我们可以通过一个赋值将它们放在两个变量中:

language, numbers_string = language_string.split(':')

这称为解包,非常pythonic。另请注意,return命令后跟两个以逗号分隔的值。结果将是包含两个值的tuple type的值:

>>> parse_language('English : 1, 2, 3')
('English', [1, 2, 3])

两个值成为唯一元组的过程称为 packing

更多功能

只有我们每个字符串只有语言...但是,我们每个字符串都有各种语言,例如'English : 1, 2, 3 ; Dutch : 5, 6, 7'。但我们知道它的解决方案,对吗?是的,一个新功能!现在,使用我们学到的所有内容:split()strip(),列表推导......

def split_languages(languages):
    return [language.strip() for language in languages.split(';')]

# Little assert to ensure it works    
assert (
        split_languages('English : 1, 2; Dutch : 5, 7') ==
                ['English : 1, 2', 'Dutch : 5, 7']
)

当然,我们只获得一个字符串列表,而不是一个字典。使用非常pythonic很容易解决...

dict构造函数(documentationdocumentation

正如您现在所做的那样,可以通过{key: value ...}语法或dict()构造函数创建dicts。构造函数有一些非常酷的行为。其中之一是从对列表中创建dicts。请考虑以下列表:

>>> l = [('key-1', 0), ('key-2', 'value'), ('key-3', 2)]

如果我们将它传递给dict构造函数,我们将得到如下所示的dict:

>>> dict(l)
{'key-3': 2, 'key-2': 'value', 'key-1': 0}

这就是parse_language()返回元组的原因:我们可以使用它来创建键对值。使用generator expressions(一种更高效,更有效的列表理解)和dict构造函数,我们可以通过这种方式从字符串中获取所有语言:

def parse_languages(languages):
    return dict(
        parse_language(language) 
        for language in split_languages(languages)
    )

# You know, let's assure everything is until now
assert (
    parse_languages('English : 1, 2; Dutch : 5, 7') == 
            {
                    'English' : [1, 2], 
                    'Dutch' : [5, 7]
            }
)

由于每一个,我们都说,"类别"有一个名字(例如" person"或" home")和一个"语言字符串"可以通过parse_languages()解析,我们的下一个pythonic步骤是使用...

另一个功能

这个新功能实际上不会有好消息:拆包将足以挽救这一天:

def parse_category(category_tuple):
    category, languages = category_tuple
    return category, parse_languages(languages)

# It is pythonic to test your functions, too! As you can see, asserts
# can help on it. They are not very popular, however... Go figure.
assert (
    parse_category( ('person', 'English : 1, 2; Dutch : 5, 7') ) ==
            (
                    'person',
                    {
                            'English' : [1, 2],
                            'Dutch' : [5, 7]
                    }
            )
)

请注意,我们的parse_category()函数返回一个元组。这是因为我们可以使用生成器表达式和dict构造函数从输入的所有元组创建一个dict。有了它,我们可以产生一个非常优雅,pythonic功能:

def parse_tuples(tuples):
    return dict(parse_category(category) for category in tuples)

# No assert now. I have typed too much, I need some coffee :(

但是我们会在这里放置这么多功能吗?在有史以来最狂热的事情之一:

模块(documentation

就我而言,我将其全部保存在名为langparse.py的文件中。现在我可以导入它并调用我们的parse_tuples()函数:

>>> import langparse
>>> tuplist = [('person', u'English : 1, 2, 3 ; Dutch : 5, 6, 7'), ('home', u'English : 8, 9, 10; Dutch: 11, 12, 13')]
>>> langparse.parse_tuples(tuplist)
{'person': {u'Dutch': [5, 6, 7], u'English': [1, 2, 3]}, 'home': {u'Dutch': [11, 12, 13], u'English': [8, 9, 10]}}

在终端中呼叫它当然只是为了测试,但天空是极限。由于它在一个模块中,我可以在其他地方使用我的所有功能。模块是如此pythonic,Zen of Python中的最后一行是对它们的致敬:

  

命名空间是一个很好的主意 - 让我们做更多的事情!

当然,不仅对他们而言,模块肯定是Python中最重要的命名空间之一。

"嗯,好吧,"我可以听到你的疑惑,"这一切都很酷而且很好,但我只想写一点(但有点复杂)的剧本!我现在应该怎么做,只是为了调用我的模块而写另一个文件?"一点也不,我的朋友!你只需要使用......

__name__ == "__main__"成语

在python中,您可以从__name__变量中检索模块的名称。它始终是模块的名称......但有一个例外:如果直接调用模块(而不是导入),则__name__的值将为"__main__"。例如,如果我创建这样一个简单的模块:

$ echo 'print(__name__)' > mymod.py

...并导入它,输出将是它的名字:

$ python -c' import mymod'     mymod

但是,如果我直接执行mymod.py,则输出为__main__

$ python mymod.py
__main__

因此,我们可以在模块的末尾添加以下行。现在,如果直接调用,它将始终执行代码,但如果导入模块,则永远不会执行:

if __name__ == "__main__":
    tuplist = [
        ('person', u'English : 1, 2, 3 ; Dutch : 5, 6, 7'),
        ('home', u'English : 8, 9, 10; Dutch: 11, 12, 13')
    ]

    print parse_tuples(tuplist)

让我们看看?这是我机器的结果:

$ python langparse.py 
{'person': {u'Dutch': [5, 6, 7], u'English': [1, 2, 3]}, 'home': {u'Dutch': [11, 12, 13], u'English': [8, 9, 10]}}

结论

Python有许多很酷的东西,使编程成为一项非常棒的任务。这些包括有用的方法,模块,列表,元组,dicts,列表推导(以及生成器表达式和字典生成器!),解包...学习所有这些习语将使一切变得更容易。但是,如果您只使用一件事,请使用功能和模块。即使你创建了太多它们,合并它们也比分割更容易。

而且,毕竟,如果你对如何做有疑问,请记住我们的最高指南Zen of Python。是的,这有点幽默,但它也是真实的。

可以看到完整的脚本in Pastebin

答案 2 :(得分:0)

def dictify(s):
    return dict((v.split(":",1)[0],v.split(":",1)[1].split(",")) for v in s.split(";"))

dict((x[0],dictify(x[1])) for x in my_tuple)

我可能会这样做......