【问题标题】:Convert list into a dictionary [duplicate]将列表转换为字典[重复]
【发布时间】:2011-10-17 13:47:58
【问题描述】:
l = ["a", "b", "c", "d", "e"]

我想将此列表转换为字典,例如:

d = {"a": "b", "c": "d", "e": ""}

所以基本上,偶数将是关键,而赔率将是值。我知道我可以用“非pythonic”的方式来做到这一点,例如带有 if 语句的 for 循环,但我相信应该有一种更“pythonic”的方式来实现这一点。所以,我感谢任何帮助:)

【问题讨论】:

    标签: python list dictionary


    【解决方案1】:

    如果你还在想什么!你不会孤单,其实没那么复杂,让我解释一下。

    如何仅使用内置函数将列表转换为字典

    我们希望将以下列表转换为字典,使用奇数条目(从 1 开始计数)作为映射到其连续偶数条目的键。

    l = ["a", "b", "c", "d", "e"]
    

    dict()

    要创建字典,我们可以根据手册使用内置的dict 函数Mapping Types,支持以下方法。

    dict(one=1, two=2)
    dict({'one': 1, 'two': 2})
    dict(zip(('one', 'two'), (1, 2)))
    dict([['two', 2], ['one', 1]])
    

    最后一个选项建议我们提供一个包含 2 个值或 (key, value) 元组的列表列表,因此我们希望将顺序列表变成:

    l = [["a", "b"], ["c", "d"], ["e",]]
    

    我们还介绍了zip函数,one of the built-in functions,手册中有解释:

    返回一个元组列表,其中第 i 个元组包含来自每个参数的第 i 个元素

    换句话说,如果我们可以将列表分为两个列表a, c, eb, d,那么zip 将完成剩下的工作。

    切片表示法

    Slicings,我们看到它与Strings 一起使用,在List section 中也更进一步,它主要使用 rangeshort slice 表示法,但这就是long slice 符号的样子以及我们可以通过 step 完成的工作:

    >>> l[::2]
    ['a', 'c', 'e']
    
    >>> l[1::2]
    ['b', 'd']
    
    >>> zip(['a', 'c', 'e'], ['b', 'd'])
    [('a', 'b'), ('c', 'd')]
    
    >>> dict(zip(l[::2], l[1::2]))
    {'a': 'b', 'c': 'd'}
    

    尽管这是理解所涉及的机制的最简单方法,但也有一个缺点,因为切片每次都是新的列表对象,从这个克隆示例中可以看出:

    >>> a = [1, 2, 3]
    >>> b = a
    >>> b
    [1, 2, 3]
    
    >>> b is a
    True
    
    >>> b = a[:]
    >>> b
    [1, 2, 3]
    
    >>> b is a
    False
    

    尽管 b 看起来像 a,但它们现在是两个独立的对象,这就是我们更喜欢使用 grouper recipe 的原因。

    石斑鱼食谱

    虽然 grouper 被解释为 itertools 模块的一部分,但它也可以与基本功能完美配合。

    一些严肃的巫术,对吗? =) 但实际上不过是香料的一点语法糖,石斑鱼配方是通过以下表达式完成的。

    *[iter(l)]*2
    

    如果有意义的话,这或多或少会转换为包含在列表中的同一迭代器的两个参数。让我们将其分解以帮助阐明。

    压缩到最短

    >>> l*2
    ['a', 'b', 'c', 'd', 'e', 'a', 'b', 'c', 'd', 'e']
    
    >>> [l]*2
    [['a', 'b', 'c', 'd', 'e'], ['a', 'b', 'c', 'd', 'e']]
    
    >>> [iter(l)]*2
    [<listiterator object at 0x100486450>, <listiterator object at 0x100486450>]
    
    >>> zip([iter(l)]*2)
    [(<listiterator object at 0x1004865d0>,),(<listiterator object at 0x1004865d0>,)]
    
    >>> zip(*[iter(l)]*2)
    [('a', 'b'), ('c', 'd')]
    
    >>> dict(zip(*[iter(l)]*2))
    {'a': 'b', 'c': 'd'}
    

    正如你所看到的,两个迭代器的地址保持不变,所以我们使用同一个迭代器,然后 zip 首先从其中获取一个键,然后是一个值,一个键和一个值,每次步进同一个迭代器来完成我们对切片所做的工作效率更高。

    您可以通过以下带有较小的 What the? 因素来完成相同的操作。

    >>> it = iter(l)     
    >>> dict(zip(it, it))
    {'a': 'b', 'c': 'd'}
    

    如果您注意到空键 e 已从所有示例中丢失,那该怎么办,这是因为 zip 选择了两个参数中最短的一个,那么我们该怎么办。

    一种解决方案可能是向奇数长度的列表添加一个空值,您可以选择使用appendif 语句来解决问题,尽管有点无聊,对吧?

    >>> if len(l) % 2:
    ...     l.append("")
    
    >>> l
    ['a', 'b', 'c', 'd', 'e', '']
    
    >>> dict(zip(*[iter(l)]*2))
    {'a': 'b', 'c': 'd', 'e': ''}
    

    现在,在您耸耸肩去输入 from itertools import izip_longest 之前,您可能会惊讶地发现它不是必需的,我们可以单独使用内置函数完成相同的,甚至更好的恕我直言。

    最长的地图

    我更喜欢使用map() function 而不是izip_longest(),它不仅使用更短的语法不需要导入,而且可以在需要时自动分配实际的None 空值。

    >>> l = ["a", "b", "c", "d", "e"]
    >>> l
    ['a', 'b', 'c', 'd', 'e']
    
    >>> dict(map(None, *[iter(l)]*2))
    {'a': 'b', 'c': 'd', 'e': None} 
    

    比较这两种方法的性能,正如 KursedMetal 指出的那样,很明显,itertools 模块在大容量上的性能远远优于 map 函数,作为对 1000 万条记录的基准显示。

    $ time python -c 'dict(map(None, *[iter(range(10000000))]*2))'
    real    0m3.755s
    user    0m2.815s
    sys     0m0.869s
    $ time python -c 'from itertools import izip_longest; dict(izip_longest(*[iter(range(10000000))]*2, fillvalue=None))'
    real    0m2.102s
    user    0m1.451s
    sys     0m0.539s
    

    但是,导入该模块的成本会影响较小的数据集,当它们开始正面交锋时,地图会更快地返回大约 10 万条记录。

    $ time python -c 'dict(map(None, *[iter(range(100))]*2))'
    real    0m0.046s
    user    0m0.029s
    sys     0m0.015s
    $ time python -c 'from itertools import izip_longest; dict(izip_longest(*[iter(range(100))]*2, fillvalue=None))'
    real    0m0.067s
    user    0m0.042s
    sys     0m0.021s
    
    $ time python -c 'dict(map(None, *[iter(range(100000))]*2))'
    real    0m0.074s
    user    0m0.050s
    sys     0m0.022s
    $ time python -c 'from itertools import izip_longest; dict(izip_longest(*[iter(range(100000))]*2, fillvalue=None))'
    real    0m0.075s
    user    0m0.047s
    sys     0m0.024s
    

    什么都看不见! =)

    开心!

    【讨论】:

    • Python 3 不支持map(None, ...),因此您的解决方案仅限于 Python 2.X
    • Python 3.4.3 支持 map(None, ...),例如dict(map(None, *[iter(l)]*2)) 返回 dict(map(None, *[iter(l)]*2)) for l = ['a', 'b', 'c' , 'd', 'e']
    【解决方案2】:

    使用通常的grouper recipe,您可以这样做:

    Python 2:

    d = dict(itertools.izip_longest(*[iter(l)] * 2, fillvalue=""))
    

    Python 3:

    d = dict(itertools.zip_longest(*[iter(l)] * 2, fillvalue=""))
    

    【讨论】:

    • 您也可以使用 d = dict(itertools.izip_longest(l[::2], l[1::2], fillvalue='')) 或在 Python 3 中使用 d = dict(itertools.zip_longest(l[::2], l[1::2], fillvalue=''))。您的版本运行良好,但对于那些没有意识到[iter(l)] * 2 创建的列表包含两个对同一个迭代器 的引用的人来说可能会有些混乱。 ([iter(l)]*2 -> [&lt;list_iterator object at 0x01383FD0&gt;, &lt;list_iterator object at 0x01383FD0&gt;])
    • @JAB:由于石斑鱼配方很常见,我认为您的方法没有足够的好处来证明创建两个l 副本是合理的(使用l[::2]l[1::2])。
    • 嗯,我一直认为切片是对象的视图,而不是实际的副本,因为 del x[:1]x[:] = [2, 3] 等用作可变序列的操作以及事实上,numpy 将数组切片实现为视图,但实际上似乎并非如此,我认为更有效的方法是d = dict(izip_longest(islice(l, 0, None, 2), islice(l, 1, None, 2), fillvalue=''))。我想知道是否有提交的功能请求,如果有,响应是什么......
    【解决方案3】:

    我会去递归:

    l = ['a', 'b', 'c', 'd', 'e', ' ']
    d = dict([(k, v) for k,v in zip (l[::2], l[1::2])])
    

    【讨论】:

    • 为什么是递归的?在我看来它是迭代的。
    • 什么变成了dict理解python.org/dev/peps/pep-0274
    • 我觉得这就够了dict(zip(l[::2], l[1::2]))。对我来说效果很好,Python 3.8.5
    【解决方案4】:

    不确定它是否对你有帮助,但它对我有用:

    l = ["a", "b", "c", "d", "e"]
    outRes = dict((l[i], l[i+1]) if i+1 < len(l) else (l[i], '') for i in xrange(len(l)))
    

    【讨论】:

    • 请注意,+1 是从 ONE 开始的,如果你的从零开始,只需删除 +1。对我来说就像一个魅力!不错!
    猜你喜欢
    • 2017-10-01
    • 2019-11-27
    • 2020-03-19
    • 2020-11-11
    • 2016-08-29
    • 1970-01-01
    • 2019-11-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多